React Lifting State Up

In the last lesson on React Child to Parent Communication you saw a child send data up to its parent. But what if two sibling components need the same data, and they can’t talk to each other directly? Lifting state up is how you fix that.

πŸ€” Why do we need to lift state up?

Picture two boxes side by side. You type in the left box, the right box should mirror the same text. But here’s why that breaks:

  • Each sibling is its own component, so one cannot reach inside the other.
  • If the left box keeps the text in its own state, then only the left box knows about it.
  • The right box has no way to read that value, because there’s no sideways path between siblings.
  • So the two boxes go out of sync. The left one updates, but the right one stays empty.

In React data only flows down, from a parent to its children. There’s no built-in path from one sibling to another.

no direct path

Parent

Left box

Right box

So if both siblings need the same value, neither one is the right place to keep it. We need a spot that both can reach.

🧩 What does β€œlifting state up” mean?

Lifting state up means moving state out of a child and up into the closest parent that both children share. That shared parent is called the common parent.

  • The common parent is the nearest component that sits above both siblings in the tree.
  • You move the useState out of the child and put it in that parent.
  • Now the parent owns the data, and a parent can pass data down to all its children.

A simple way to picture it: two kids both want to look at one photo, so you put the photo on the table between them where both can see it. The table is the common parent.

One owner, shared by all

After lifting, only the parent holds the state. Each child just receives the value as a prop. There is one single source of truth, and everyone reads from it.

❌ Before: state stuck in one sibling

Let’s see the broken version first. Here the left box keeps the text in its own state, and the right box tries to show it but can’t.

// ❌ State is stuck inside LeftBox
function LeftBox() {
const [text, setText] = useState("");
return (
<input value={text} onChange={(e) => setText(e.target.value)} />
);
}
function RightBox() {
// RightBox has no way to read LeftBox's text. There is no path.
return <p>You typed: ???</p>;
}
function App() {
return (
<>
<LeftBox />
<RightBox />
</>
);
}

So what goes wrong here?

  • The text value lives inside LeftBox, so it’s private to that component.
  • RightBox is a sibling, not a parent or child of LeftBox, so it cannot reach that state.
  • So RightBox can only show ???, and the two boxes never stay in sync.

The state is in the wrong place. It’s too low in the tree for both siblings to share it.

βœ… After: lift the state to the parent

Now the fix. We move the state up into App, the common parent. Then App passes the value down to both children, and passes a setter down so a child can change it.

import { useState } from "react";
function App() {
// βœ… State now lives in the common parent
const [text, setText] = useState("");
return (
<>
<LeftBox text={text} onChange={setText} />
<RightBox text={text} />
</>
);
}
function LeftBox({ text, onChange }) {
return (
<input value={text} onChange={(e) => onChange(e.target.value)} />
);
}
function RightBox({ text }) {
return <p>You typed: {text}</p>;
}

Here is the flow, step by step:

  • App owns the text state now, because it sits above both boxes.
  • App sends text down to both children as a prop, so both read the same value.
  • App also sends setText down to LeftBox as onChange, so the input can update the shared value.
  • You type in LeftBox. It calls onChange, which is really setText, so the state in App changes.
  • App re-renders, sends the new text to both boxes, and RightBox instantly shows it too.

So now both boxes always match. That’s synchronized components, and the parent is keeping them in sync for you.

Output

[ Hello ] <- you type here (LeftBox)
You typed: Hello <- this updates live (RightBox)

πŸ” The pattern: down and up together

Lifting state up is really two pieces you already know, working together. Props go down, a callback comes up.

  • Value goes down: the parent passes the current value to each child as a prop.
  • Update comes up: the parent passes a setter (or a handler) down, and the child calls it to change the value.
  • The child never owns the data. It just reads the value and asks the parent to change it.

This is the same two-way connection from the last two lessons, now used to keep siblings in sync.

value as prop

value as prop

calls setter / handler

Parent - owns state

Child A

Child B

So the parent is the single owner. Children read the value going down, and send changes back up by calling the function the parent gave them.

πŸͺœ How to lift state, step by step

When you hit this situation in real code, follow this routine. It’s the same every time.

  • Find the common parent. Look up the tree for the nearest component that sits above both siblings.
  • Move useState there. Cut the useState out of the child and paste it into that parent.
  • Pass the value down. Send the state value to each child that needs to read it, as a prop.
  • Pass the setter down. Send the setter (or a small handler) to the child that needs to change it.
  • Update through the prop. In the child, call that function instead of changing its own state.

Don't lift everything by reflex

Only lift state that two or more components actually share. If just one component needs a value, keep the state inside that component. Lifting state that nobody else uses only adds extra props and makes the code harder to follow.

🧩 What You’ve Learned

  • βœ… Siblings cannot share data directly, because React data only flows down from parent to child
  • βœ… Lifting state up means moving state out of a child and into the closest common parent
  • βœ… The common parent is the nearest component that sits above both siblings
  • βœ… The parent owns the state and passes the value down to each child as a prop
  • βœ… The parent also passes a setter down, so a child can ask it to change the shared value
  • βœ… This keeps sibling components synchronized, with one single source of truth
  • βœ… Only lift state that is actually shared β€” keep private data inside its own component

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    Why can't two sibling components share data directly in React?

    Why: React uses one-way data flow. Data moves down from a parent to its children. There is no built-in path from one sibling straight to another, so shared data has to live in a common parent.

  2. 2

    What does 'lifting state up' actually mean?

    Why: Lifting state up means you take the state out of a child and move it up into the nearest common parent, so that parent can pass the value down to all the children that need it.

  3. 3

    After you lift state to the parent, how does a child change that shared value?

    Why: The parent passes a setter (or a handler) down as a prop. The child calls that function to ask the parent to update the shared state. The child never owns or reassigns the data itself.

  4. 4

    When should you NOT lift state up?

    Why: Lift state only when two or more components share it. If just one component needs a value, keep that state inside that component. Lifting unshared state only adds extra props and clutter.

πŸš€ What’s Next?

You now know how to lift state into a common parent so siblings can share it. Next you’ll go a step further and see how to design shared state cleanly across a bigger tree of components, so your app stays easy to follow as it grows.

React Sharing State Between Components

Share & Connect