A React component re-renders when its own state changes, when its parent re-renders, or when a context it reads changes. Rendering means React calls your component function again to get fresh JSX; it then updates only the parts of the page that actually changed.
Open the Console under the preview and click the button. All three components log, including Title, which takes no props at all. It rendered because its parent did.
What triggers a render
React renders a component for exactly these reasons:
- The first render. The app starts, or the component appears in the tree for the first time.
- Its state changed. You called a setter from
useStateor a dispatch fromuseReducerwith a new value. - Its parent rendered. By default, when a component renders, every component it returns renders too, all the way down.
- A context it reads changed. A component that calls
useContext(SomeContext)renders when the nearest provider passes a newvalue(see useContext).
A common belief is that a component renders "because its props changed". That gets the cause backwards. Props are arguments the parent passes while it renders, so new props can only arrive when the parent renders. And the parent rendering is enough on its own: Title above has no props and still renders every time.
Setting state to the value it already has (compared with Object.is) does not start a render of the children. React may still call that one component once before it notices nothing changed, but it throws the result away.
Render and commit
Every update goes through two phases:
- Render. React calls your components. They return JSX, which is just objects describing what the screen should show. Nothing on the page changes yet, which is why render must be pure: no DOM writes, no requests, no changing variables outside the component.
- Commit. React compares the new output with the previous one and applies the differences to the DOM: it inserts, removes or updates only the nodes that changed. Then the browser paints, and after that React runs your effects.
The example below renders on every click, but React keeps the same <input> element. An effect runs after each commit and checks that.
Type something into the input and click a few times. The text you typed stays, because React never replaced the input: the only DOM change per click is the number inside the <p>. The log also shows the order, render first and the effect after the commit.
The virtual DOM, plainly
"Virtual DOM" is the popular name for the objects your components return. <p>Items: {count}</p> compiles to a call that creates an object like { type: 'p', props: { children: ['Items: ', 1] } }. After a render, React walks the new tree of these objects next to the previous one, and where the type and position match it keeps the existing DOM node and updates only changed attributes and text. This comparison is called reconciliation.
The term is loose for two reasons. React does not keep a second copy of the DOM and diff two DOMs: it compares element objects against its own internal tree of components (the fiber tree). And the same process drives targets that have no DOM at all, such as React Native. The React docs mostly avoid the phrase and talk about rendering and committing instead. What matters in practice is the consequence: rendering is cheap compared with DOM work, because most renders end in a small DOM update or none.
Two rules follow from how the comparison works. A different element type in the same place (a <div> replaced by a <section>, or ComponentA by ComponentB) destroys the old subtree and its state. And in lists, key tells React which item is which, so it can move nodes instead of rebuilding them.
Stopping renders you do not need
Most extra renders cost nothing you can notice. When one does hurt, for example a big list that renders on every keystroke elsewhere on the page, try these in order.
Move state down
If only one small part of the screen uses a piece of state, keep that state in a component that wraps only that part. This version re-renders ProductList on every keystroke:
export default function App() {
const [text, setText] = useState('');
return (
<>
<input value={text} onChange={(e) => setText(e.target.value)} />
<ProductList />
</>
);
}
Move the input and its state into their own component and the list is no longer inside the component that renders:
Type a few letters: only SearchBox logs.
Pass children instead
Sometimes the state has to live in a wrapper around the expensive part, such as a panel that toggles a highlight. Have the wrapper accept children. The parent creates the child elements, and since the parent does not render when the wrapper's state changes, those elements are the same objects as before, so React skips them.
Click the toggle and only Highlighter logs. Now move <Article /> inside Highlighter's JSX in place of {children}, and every click logs render Article too.
Memoize
When neither restructuring fits, wrap the child in memo. React then compares its props with the previous ones and skips the render when they are all the same. Objects and functions created during render are new every time, so memo usually comes with useMemo or useCallback for those props. The React.memo page shows this with running examples.
import { memo } from 'react';
const ProductList = memo(function ProductList({ category }) {
// skipped while category stays the same
});
React Compiler can add this kind of memoization for you at build time, but restructuring state is still the first thing to try: it removes the work instead of caching it.
Renders are usually fine
A render is a function call that returns objects. React runs thousands of them quickly, and a render that produces the same output changes no DOM. Do not add memo everywhere "to be safe": each comparison has its own cost and makes code harder to read. Measure first. The React DevTools Profiler shows which components rendered, why, and how long each took.
Frequently Asked Questions
What causes a React component to re-render?
Three things: its own state changes, its parent renders, or a context it reads with useContext gets a new value. Props are not a separate trigger: new props only arrive because the parent rendered.
Does a child re-render when the parent re-renders?
Yes, by default every component inside a rendering parent renders too, even if its props did not change. Wrapping the child in memo lets React skip it when its props are the same as last time.
What is the virtual DOM in React?
It is a loose name for the tree of plain JavaScript objects (React elements) that your components return. React compares the new tree with the previous one and changes only the real DOM nodes that differ.
Is re-rendering bad for performance?
Usually not. A render is a function call that produces objects, and React only touches the DOM where the output changed. Optimize when a render is measurably slow, for example with the React DevTools Profiler.
What is the difference between render and commit?
Rendering is React calling your components to find out what the screen should look like. Committing is React applying the differences to the DOM. A render that produces the same output commits no DOM changes.