Global state is tempting, but most values do not need it. A value should live as close as possible to the component that uses it, and move outward only when two or more components truly share it. The default should be local, and every move outward needs a reason.
Start local
A value used by one component should stay in that component with useState. Start with the simplest form and let the component tell you when it has grown too small.
function SearchBox() {
const [query, setQuery] = useState("");
return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
}The query only matters to the input, so there is nothing to share. Moving it to a global store would add indirection without adding value. A good question before adding a store is: which other component needs this value right now?
Lift state when two components share it
When a second component needs the same value, move it to the closest common parent and pass it down.
function App() {
const [query, setQuery] = useState("");
return (
<div>
<SearchBox query={query} onChange={setQuery} />
<Results query={query} />
</div>
);
}The parent owns query, and both children receive it as props. This is lifting state, and a fuller example is in lifting state up in React.
Lift only as far as the shared parent; lifting higher spreads the value through components that do not use it. Keep the value at the lowest level where every user of it can still see it.
Derive instead of store
Do not store values you can compute during render. Two pieces of state that always move together are usually one piece of state plus a derived value.
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const fullName = firstName + " " + lastName;fullName is calculated on every render, so it can never fall out of sync. Storing it separately would mean updating it in two places. The same rule applies to any repeated data: keep the source and compute the rest.
Compose instead of globalizing
Passing a component down as a prop or using children avoids lifting state at all. The child keeps its own state, and the parent never needs to know about it.
function Layout({ sidebar }) {
return <div>{sidebar}<main>Content</main></div>;
}
function App() {
return <Layout sidebar={<Sidebar />} />;
}Sidebar owns its own open and closed state, so Layout renders it without ever reading that state. Moving state down is often better than moving it up. This pattern, sometimes called composition instead of configuration, keeps state exactly where it is used.
The cost of globalizing too early
Global state has real costs. Every consumer subscribes or re-renders, the store becomes a shared dependency that slows refactors, and new developers must learn its shape before touching a small feature.
Those costs are fine when the state is genuinely shared. They are pure overhead when the state is used in one place.
A store that holds five values used once each is five indirections with no payoff. Each extra consumer also makes the store harder to delete later.
Signs you are over-globalizing
- One component reads a value that nothing else uses.
- Props travel through several layers that only forward them.
- A store field changes only from a single place.
- New contributors must learn the store before editing a small feature.
Any one sign is a hint, not a rule. Several together usually mean the state is more global than it needs to be. Run down the list before adding a new global value.
When global state is the right call
A store earns its place when many distant components read and write the same data, or when middleware and devtools matter. The signs are props that travel through many layers and the same data updated in five places.
For that situation, the state management decision guide walks through the options. The split between client and server data is in client state vs server state in React. Start local and refactor toward a store when the pain appears, not before.
Rune AI
Key Insights
- Start with useState in the component that uses the value.
- Lift state to the closest common parent when two components share it.
- Derive values during render instead of storing them.
- Add a store only when many distant components need the same data.
Frequently Asked Questions
When should I lift state instead of using a store?
What is derived state?
When is a global store worth it?
Conclusion
Most state does not need to be global. Keep values local, lift them only when components share them, and derive what you can during render.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.