Lifting state up
A text input sits next to a preview that needs to show whatever's been typed into it. Both live in the same component tree, but state declared inside one component is invisible to its neighbors, so neither the input nor the preview can read or change what the other is holding. The fix is to move that state up to the closest component both of them share, then pass the value down to each as a prop. This is called lifting state up.
Here's that pair, sharing one value:
import { useState } from 'react'
function Form() {
const [text, setText] = useState('')
return (
<>
<TextInput text={text} onChange={setText} />
<TextDisplay text={text} />
</>
)
}
function TextInput({ text, onChange }) {
return <input value={text} onChange={e => onChange(e.target.value)} />
}
function TextDisplay({ text }) {
return <p>You typed: {text}</p>
}Form is the closest common parent of TextInput and TextDisplay, so that's where text lives. It passes text down to both, and it passes setText down to TextInput under the name onChange. When someone types, TextInput calls onChange with the new value, Form's state updates, and Form re-renders. That re-render sends the new text back down to both children, so TextDisplay shows exactly what's in the input without holding any state of its own.
Keep state as low as it can live
Lifting state up is a move you make only when you need it. The guiding rule is state colocation: keep each piece of state as low in the tree as it can live, close to the component that actually uses it, and lift it only when something else needs to read or change it too. A component with state nobody else touches should keep that state to itself.
This matters because lifting has a cost. Every level a piece of state moves up, the parent has to pass it back down again as a prop, and every component in between has to accept and forward that prop even if it never uses the value itself. Start local. Move state up only as far as the nearest component that actually needs to share it, and no further.
Passing the setter down
Notice that Form doesn't only pass text down. It passes setText down too, as the onChange prop. This is the other half of lifting state up: the component that owns the state hands out a way to change it via props, and the child calls that function instead of managing the value itself. TextInput never sees setText directly. It calls whatever function it was given, and the parent decides what that function actually does.
For state that's shared between components nested many levels apart, passing props down through each layer stops being practical. React has tools for that case, Context among them, and the Hooks chapter covers useContext, the hook that reads a value without threading it through every component in between. That's beyond what this chapter covers; the pattern above is the one to reach for first.
Next up: Effects, where you'll use useEffect to handle work that happens outside of rendering.

