Menu

Lifting State Up in React: Share State Between Components

Lifting state up means moving state out of two components and into their closest common parent, which passes the value and a setter back down as props. Learn when to lift, how to do it in three steps, and when to keep state local instead.

This page includes runnable editors - edit, run, and see output instantly.

Lifting state up means moving state out of the components that need to share it and into their closest common parent. The parent keeps the value, passes it down as a prop, and passes a function the children call to change it, so every child renders from one copy of the state.

Click "Show" on any panel: it opens and the one that was open closes. Only one panel can be open at a time because the panels do not decide that for themselves. App holds openIndex, and each Panel only receives isOpen and an onOpen function.

The problem: state that should agree

Start from the obvious version, where every panel owns its own isOpen state. Each panel works, but the panels know nothing about each other, so nothing stops two of them from being open together.

Open both panels: they stay open together. State in React is private to the component that declares it, so a sibling cannot read it or reset it. When two components need to agree, the state has to live above both of them.

Lifting state in three steps

Turning the second example into the first takes three edits.

  1. Remove the state from the child. Delete useState in Panel and read isOpen from props instead. The child no longer decides whether it is open.
  2. Pass the value and a way to change it from the parent. Panel gets isOpen and an onOpen callback. The child calls onOpen() when its button is clicked; it does not know what the parent does with that.
  3. Add the state to the common parent. App declares openIndex and turns it into props for each panel: isOpen={openIndex === 1} and onOpen={() => setOpenIndex(1)}.

The closest common parent is the lowest component that renders all the components that need the state. Here that is App. If the panels sat inside a Faq component, the state would go in Faq, not higher.

After the lift, Panel is controlled by its parent in the same sense as a controlled input: it shows what the props say and reports changes through a callback. The page on controlled vs uncontrolled components covers the same idea for form elements.

A single source of truth

When two parts of the screen show the same fact, store that fact once and compute everything else from it. A temperature converter is the classic case: the Celsius and Fahrenheit inputs must always agree, so they cannot each keep their own number.

Type in either field and the other one follows. The state is one fact: the number the user last typed and which scale it was in. The other field is computed from it during render, so the field you are typing in always keeps exactly what you typed. Change the starting state to { value: '212', scale: 'f' } and the message below the inputs switches to "Water boils."

Keeping a Celsius number and a Fahrenheit number in two pieces of state would mean every handler has to update both, and the first time one handler forgets, the two inputs disagree. One stored value cannot disagree with itself.

Passing the setter down

A child can change the parent's state only through a function the parent gives it. You can pass the setter itself (onSelect={setColor}) or a function that does more (onOpen={() => setOpenIndex(1)}). Giving the prop an event-style name such as onSelect or onChange, rather than setSelected, keeps the child unaware of how the parent stores the value, so the parent can change that later without touching the child.

ColorPicker and Preview never talk to each other. The picker reports a choice up, App stores it, and the new value flows down to both.

When not to lift state

Lifting has a cost. When state lives in a parent, every change renders the parent and, by default, all of its children, including ones that do not use the state. Lift only as high as the closest common parent, and leave state that only one component uses inside that component.

Type in the box and watch the console: only SearchBox renders on each keystroke. Now move query up into App and pass it to SearchBox as props. Every keystroke then logs ProductList too, even though the list does not use the query. If the list did filter by the query, lifting would be the right call, since both components would then depend on the same value.

The question to ask is "who needs to read this value?" If the answer is one component, the state stays there. If it is several, it goes to their closest common parent.

When lifting goes too far

Sometimes the closest common parent is far up the tree, and the value has to be passed through several components that only hand it on. That is prop drilling. A few layers of props are fine and easy to follow. When the same value travels through many layers, or almost every component needs it (the signed in user, the theme, the language), read it with useContext instead of passing it by hand. Context changes how the value reaches the children; the state itself still lives in one parent, so it is still lifted.

Frequently Asked Questions

What does lifting state up mean in React?

Moving a piece of state from the components that use it into their closest common parent. The parent owns the state and passes the value, plus a function to change it, down to the children as props.

How do two sibling components share state in React?

Siblings cannot read each other's state. Put the state in their shared parent, pass the value to both, and pass a setter (or a handler like onChange) to the one that changes it. Both siblings then render from the same value.

How does a child component update the parent's state?

The parent passes a function as a prop, for example onSelect={setSelected} or onSelect={(id) => setSelected(id)}, and the child calls it. The state stays in the parent; the child only asks for the change.

When should I not lift state up?

When only one component uses the state. Lifting it higher than needed makes the parent render on every change and spreads props through components that do not care. Keep state as close as possible to where it is used.

What is the alternative to lifting state up too far?

If you find yourself passing the same props through many layers, read the shared value with context (useContext) instead, or restructure so the components that need it sit closer together.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED