Colocating State: Keep State Close to Where It's Used
Reduce complexity by moving state into the component that actually needs it.
Introduction
Colocating state means keeping state as close as possible to the component that uses it. This reduces prop drilling, keeps parents lean, and clarifies ownership.
Why this matters
When state lives higher than necessary, parents become bloated and props trickle down multiple levels. That makes components harder to read and reason about. Colocation keeps state next to the UI that owns it.
The problem
It’s tempting to keep state high up in the tree and pass it down everywhere. But when only one component actually needs that state, the parent does extra work and the child becomes less reusable.
Inefficient approach
State is lifted unnecessarily high, causing prop drilling when only one component needs it:
import { useState } from "react";
function ToggleSection({ isVisible, setIsVisible }) {
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? "Hide" : "Show"} Content
</button>
{isVisible && (
<div>
<p>This content can be toggled!</p>
</div>
)}
</div>
);
}
function App() {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<h1>Colocating State Example</h1>
<ToggleSection isVisible={isVisible} setIsVisible={setIsVisible} />
</div>
);
}
export default App;
The solution
Move the state into ToggleSection. The component becomes self‑contained and easier to reuse, and the parent gets simpler by default.
import { useState } from "react";
function ToggleSection() {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? "Hide" : "Show"} Content
</button>
{isVisible && (
<div>
<p>This content can be toggled!</p>
</div>
)}
</div>
);
}
function App() {
return (
<div>
<h1>Colocating State Example</h1>
<ToggleSection />
</div>
);
}
Step-by-step
- Remove
isVisiblestate from the parent. - Add
const [isVisible, setIsVisible] = useState(false)insideToggleSection. - Wire the button to toggle local state.
- Render content conditionally based on local
isVisible. - Keep the parent simple—no props needed for visibility control.
When not to colocate
- Multiple siblings need to read or update the same state. In that case, lift state up to the nearest common parent.
- Many deep descendants need the value. Consider Context to avoid drilling.
Tips
- Start by colocating state with the smallest component that needs it. Lift only when duplication appears.
- Controlled components (like inputs) should manage their own transient UI state unless a parent truly needs to coordinate it.
- Keep public interfaces small. If a child must expose control, consider a callback prop like
onTogglerather than pushing all state up.
Takeaway
Colocating state cuts down prop noise and clarifies ownership. It keeps parents lean and components easy to reuse.
Knowledge base
Problem snippet:
function App() {
const [isVisible, setIsVisible] = useState(false);
return <ToggleSection isVisible={isVisible} setIsVisible={setIsVisible} />;
}
Solution snippet:
function ToggleSection() {
const [isVisible, setIsVisible] = useState(false);
// ...
}