Global vs Local State
In this page:
What Counts as Local State
Local state lives entirely inside one component, created with useState or useReducer, and disappears when that component unmounts. A single input's current text, or whether a dropdown is open, are classic examples — nothing outside that component needs to know or care.
Note: If you can't think of any OTHER component that would need this value, it's a strong sign it should stay local.
Warning: Local state resets every time its component unmounts and remounts — don't rely on it to persist across navigation unless that's the deliberate design.
Example: What Counts as Local State
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function SearchBox() {
const [text, setText] = React.useState("");
return <input value={text} onChange={e => setText(e.target.value)} />;
}
ReactDOM.createRoot(document.getElementById('root')).render(<SearchBox />);
</script>
</body>
</html>
Lifting State Up Before Going Global
When two sibling components need to share and stay in sync on the same value, the state doesn't need to go fully global — moving it up to their closest common parent component (and passing it down as props) is often enough. This is called 'lifting state up'.
Note: Lifting state up is the natural first step; only reach for Context/Redux/Zustand once prop-passing becomes genuinely awkward across many layers.
Warning: Lifting state too far up 'just in case' (all the way to the app root) when only two nearby siblings need it adds unnecessary prop-passing through everything in between.
Example: Lifting State Up Before Going Global
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function Parent() {
const [value, setValue] = React.useState("");
return (
<div>
<input value={value} onChange={e => setValue(e.target.value)} />
<p>Sibling sees: {value}</p>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<Parent />);
</script>
</body>
</html>
When State Truly Needs to Be Global
State becomes a good candidate for global management when it's needed by many components scattered across unrelated branches of the tree — like the current logged-in user, app theme, or a shopping cart accessible from both a product page and a checkout page far apart in the component hierarchy.
Note: A useful test: if lifting state up would require passing it through five or more unrelated component layers, that's a sign to go global instead.
Warning: Going global for state used by only two nearby components adds unnecessary indirection compared to simply lifting it up.
Example: When State Truly Needs to Be Global
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// Global: current user needed by Header, Sidebar, and Checkout,
// which don't share a close common parent.
const UserContext = React.createContext(null);
function Header() {
const user = React.useContext(UserContext);
return <p>Logged in as: {user || "Guest"}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(
<UserContext.Provider value="Asha"><Header /></UserContext.Provider>
);
</script>
</body>
</html>
- Making everything global 'to be safe', which makes components harder to reuse and reason about in isolation.
- Making everything local when two sibling components actually need the same synchronized value.
- Not recognizing when state has organically become needed in multiple places and should be lifted or moved to shared state.
- Local state lives inside one component via useState/useReducer and isn't visible outside it.
- Global state is accessible from anywhere in the app, via Context, Redux, Zustand, or similar tools.
- Lifting state up (moving it to a shared parent) is often enough before reaching for true global state.
- The right default is to keep state as local as possible, only widening its scope when actually needed.
No React-version restriction — this is an architectural pattern, not a specific API.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: