How to Avoid Global State When Local State Is Enough

Know when local state is enough in React. Keep values in components, lift state when needed, and avoid adding a global store too early.

6 min read

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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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

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.
RunePowered by Rune AI

Frequently Asked Questions

When should I lift state instead of using a store?

When two nearby components share a value. Move it to their closest common parent and pass it down as props.

What is derived state?

A value computed during render from existing props or state. It never needs its own state variable.

When is a global store worth it?

When many distant components read and write the same data, or when middleware and devtools matter.

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.