0011 — State management has a single answer
Server state is RSC and server actions. URL state is nuqs. Local state is useState. Everything else is a lint error.
Context
State management is where a codebase accumulates archaeology. One feature uses Redux because it was written in 2022, another uses SWR because someone liked it, a third uses Context holding server data. All three work. Together they mean nobody can answer "where does this value live?" without reading the file.
Decision
One answer per kind of state, enforced by no-restricted-imports:
- Server state — RSC and server actions
- URL state —
nuqs - Local state —
useState - Client-global non-server state — Zustand, and only in
src/features/<name>/store.ts
Redux, MobX, Jotai, Recoil, SWR and Context-holding-server-data are banned. Each ban carries its own message; SWR's is the most substantive: "Read in a Server Component; use TanStack Query in a feature hook if you truly need client fetching."
What is honestly argued, and what is not
The principle is single answer beats best answer. A codebase where every developer picks their favourite is worse than one that picks adequately and picks once.
There is no argument anywhere that Jotai is worse than Zustand, and this page is not going to invent one. The rejections are a consequence of the single-answer rule, not a verdict on each library.
Zustand is confined rather than banned, which is the one place the rule bends — a feature store is a real need and the boundary keeps it from becoming an alternative to server state.
The nuqs incident
The positive case for nuqs in a list view is documented as three non-negotiable
options — shallow: false, startTransition, clearOnDefault — each with a
one-line reason.
The sharper story is what its absence did. A missing nuqs adapter reached
production: the page threw before painting, and the entire e2e suite stayed green
because it only ever asserted that signed-out visitors were redirected to
/sign-in. No test rendered an authenticated page at all.
That is why the signed-in Playwright fixture exists, and why the rule is render real pages, do not assert redirects. The state library is almost incidental to that lesson; the testing gap it exposed was the expensive part.
0010 — Offset pagination, deliberately
Cursor pagination is better at scale and worse at everything a starting product needs.
0012 — Integrations are seams, and stay unconfigured until they earn it
Unconfigured is a supported state that fails at the call site with the variable's name — not at build time, and not with an opaque 401.