Local State vs Global State in React & Next.js β When to Use What (and How to Avoid Unnecessary Re-renders)
π§ Introduction
State management in React and Next.js isnβt about picking a single βbestβ approach β itβs about using the right tool for the right job. Poor state placement leads to performance issues, messy code, and unnecessary re-renders.
In this post, weβll break down:
- When to use local state
- When to use global state
- How to avoid unnecessary re-renders
- A simple decision guide you can follow in real projects
β
When to Use Local State (useState)
Local state belongs in a component when:
- The data is needed only in that component
- It does not affect other parts of the UI
- It is short-lived or UI-specific
Example 1 β Form input state
jsconst [email, setEmail] = useState("");
This should stay local because only this form needs it.
Example 2 β Modal visibility
jsconst [isOpen, setIsOpen] = useState(false);
No need to store this globally unless multiple components control the same modal.
Rule of thumb: If lifting the state to a parent or global store doesnβt provide any benefit β keep it local.
π When to Use Global State (Redux, Zustand, Recoil, Context API)
Use global state when:
- Multiple components need the same data
- State must persist across Next.js pages
- Data is shared across unrelated components
Example 1 β User authentication
jsconst user = useStore(state => state.user);
Used in Navbar, Dashboard, Profile, and API calls β perfect for global state.
Example 2 β Theme (dark/light mode)
jsconst theme = useStore(state => state.theme);
Accessed across the entire app.
Example 3 β Shopping cart
jsconst cartItems = useStore(state => state.cart);
Used in Navbar, Checkout, and Product pages.
Rule of thumb: If three or more components need the same data β make it global.
β‘ How to Avoid Unnecessary Re-renders (Performance Tips)
1. Avoid overusing Context for frequently changing state
β Bad:
jsconst App = () => ( <AppContext.Provider value={{ count, setCount }}>
This makes every consumer re-render when count changes.
β Better: Use Zustand or Redux for high-frequency updates.
2. Use memoization where needed
jsconst MemoizedComponent = React.memo(Component);
Prevents re-rendering when props havenβt changed.
3. Split state properly
β Bad:
jsconst [state, setState] = useState({ name: "", age: 0 });
β Better:
jsconst [name, setName] = useState(""); const [age, setAge] = useState(0);
This prevents re-renders when only one value changes.
4. Avoid passing new object references
β Bad:
js<Component config={{ theme: "dark" }} />
β Better:
jsconst config = useMemo(() => ({ theme: "dark" }), []); <Component config={config} />
π§ Simple Decision Guide
- Component-only UI behavior β Local state
- Shared across pages/components β Global state
- Rapidly changing data β Avoid Context, prefer Zustand/Redux
- Rarely changing data β Context is fine