Lifting State Up
Share state between sibling components by moving it to a common parent
Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.
UpgradeKnowledge Debt detected
You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.
Explanation
When two sibling components need to share state, to their closest common parent.
Problem: sibling A needs sibling B's state
jsx // Can't access sibling's state directly function FilterInput() { /* has searchTerm state */ } function ResultList() { /* needs searchTerm */ }
Solution: move state up to the parent
jsx function SearchPage() { const [searchTerm, setSearchTerm] = useState(''); // lifted here return ( <> <FilterInput searchTerm={searchTerm} onSearch={setSearchTerm} /> <ResultList searchTerm={searchTerm} /> </> ); } function FilterInput({ searchTerm, onSearch }) { return ( <input value={searchTerm} onChange={e => onSearch(e.target.value)} /> ); } function ResultList({ searchTerm }) { const filtered = allItems.filter(i => i.name.includes(searchTerm)); return <ul>{filtered.map(i => <li key={i.id}>{i.name}</li>)}</ul>; }
The pattern:
- 1. Remove state from child components
- 2. Move it to the nearest common ancestor
- 3. Pass state down as props
- 4. Pass setter callbacks down as props
This keeps data flow predictable — one source of truth.
Examples
Shared counter between two components
Display reads state; Controls changes it via callback — both via parent
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<Display count={count} />
<Controls count={count} onIncrement={() => setCount(c => c + 1)} />
</>
);
}
function Display({ count }) {
return <h2>Count: {count}</h2>;
}
function Controls({ onIncrement }) {
return <button onClick={onIncrement}>+1</button>;
}Tab selection lifted to layout
The parent owns which tab is active; both child components receive it as a prop
function Layout() {
const [activeTab, setActiveTab] = useState('overview');
return (
<div>
<TabBar activeTab={activeTab} onTabChange={setActiveTab} />
<TabContent tab={activeTab} />
</div>
);
}How well did you understand this?
Next in React
useContext