Opinion
You probably don't need a state management library
Most of what we call state is really server data or the URL. Sort those out first and the remaining state is small enough for React itself.
Every new React project begins with the same question: which state library should we use? I think the question is usually asked too early, and about the wrong thing.
Most state isn't yours
Look at the global store in an average app. Much of it is a copy of data that lives on a server: users, products, comments. That is a cache, and it has cache problems, such as stale data, refetching and invalidation. A tool built for server data handles that better than a general store, and a framework with data fetching built in handles it for free.
What remains is smaller than you expect.
A lot of the rest belongs in the URL
Filters, the current tab, the search query and the page number all have something in common: users expect to share them and use the back button on them. Putting them in the URL gives you both for nothing.
"use client";
import { useRouter, useSearchParams } from "next/navigation";
export function SortSelect() {
const router = useRouter();
const params = useSearchParams();
function onChange(value: string) {
const next = new URLSearchParams(params);
next.set("sort", value);
router.replace(`?${next.toString()}`);
}
return (
<select value={params.get("sort") ?? "newest"} onChange={(e) => onChange(e.target.value)}>
<option value="newest">Newest</option>
<option value="popular">Most popular</option>
</select>
);
}No store, no provider, and the state survives a refresh.
What is left is genuinely local
Whether a menu is open, what the user has typed into a form so far, which row is being edited. That is useState or useReducer, kept next to the component that uses it. When two distant components need the same value, lift it to their nearest shared parent, or pass it through context if it changes rarely.
When a library is the right call
I am not arguing that these tools are bad. They earn their place when:
- many unrelated components update the same client-side state at high frequency, such as an editor or a canvas;
- you need undo and redo, or other features that depend on a predictable log of changes;
- a team is large enough that a strict, shared pattern prevents more bugs than it causes.
That list describes some apps, but not most of them.
The cost people forget
A state library is a dependency, but it is also a vocabulary your team must learn, and a place where data can quietly become inconsistent with the server. Starting with plain React keeps the app easy to read, and you can adopt a library later at the exact spot where the pain appears. Going the other way, removing a library from a finished app, is much harder.
So my default is simple: fetch with your framework, store shareable state in the URL, keep everything else local, and add a library only when you can name the problem it solves. I'd be curious where you think that default breaks down, so tell me in the comments.
Comments
Keep reading
Debounce a search box in React, the right way
Fire one request when the user stops typing, not one per keystroke. A small hook, plus the cancellation detail most examples skip.