Blog post
The Hardest Part of Next.js Was Learning Where State Should Not Live
Why server-first Next.js development is less about state management libraries and more about putting authority in the right place.
The Hardest Part of Next.js Was Learning Where State Should Not Live
The hardest part of state management was not choosing a state library.
It was learning that much of the state I was tempted to manage in the browser should not live there at all.
Next.js pushed me into that realization. AI-assisted coding made the lesson more visible.
Agents often reach for familiar React patterns: useEffect, client-side fetches, local state, optimistic UI, global context. Those patterns are not wrong. But in a server-first application, they are not always the right default.
The question is not "where can this state live?"
The better question is: "who has authority over this state?"
Not all state is equal
Some state belongs in the browser.
A dropdown is open.
A form field is being edited.
A modal is visible.
A tab is selected.
That is UI state. Keeping it close to the component usually makes sense.
But other state is different.
Who can access this record?
Which tenant is active?
What data is official?
What permissions apply?
What changed after a mutation?
That state has authority behind it. It should not be casually reconstructed in the client.
The AI default can be too client-heavy
A lot of public React code teaches one pattern:
useEffect(() => {
fetch('/api/data').then(...)
}, [])
AI has seen that pattern everywhere.
So unless I guide it, it may reach for client-side fetching even when a server component would be simpler, safer, and more consistent.
It may introduce local state to mirror server truth.
It may add a loading state where the server could render the initial data.
It may put logic in the component that belongs in an application use case.
None of this is malicious. It is just pattern completion.
That means I need architectural defaults strong enough for the agent to follow.
Server components changed the default
Server components make a different default possible.
Fetch authoritative data on the server.
Keep secrets on the server.
Apply access rules on the server.
Render the initial view from server truth.
Use client components only where interaction requires them.
That does not eliminate client state. It makes client state more specific.
The browser handles interaction.
The server owns authority.
Server actions and mutations
The same distinction matters for mutations.
Submitting a form is not just a UI event. It may change server state, trigger validation, enforce permissions, invalidate caches, and return a new view of the world.
If the mutation is treated as a client-side convenience, the architecture gets blurry.
A better model is:
- the client captures user intent;
- the server validates and executes;
- the application layer owns the use case;
- caches are invalidated deliberately;
- the UI reflects the new server truth.
That sounds more formal, but it reduces confusion.
Cache invalidation is part of the design
In a server-first app, state management is not only about variables.
It is also about freshness.
What data can be cached?
What must be revalidated after a mutation?
Which pages depend on this change?
Which user or tenant scope does the cached data belong to?
AI can generate a working page without answering those questions. But a working page is not enough if it shows stale or incorrectly scoped data.
The principle
Less client state is not minimalism.
It is a way to keep authority where it belongs.
The browser is excellent for interaction. The server is where identity, permissions, data integrity, and durable state should be enforced.
The harder lesson was not how to manage more state.
It was how to stop putting state in places that should never have been responsible for it.
Continue exploring
Follow the same line of thought through themes, tags, or a broader local search across the archive.
Keep following the thread.
AI Made UI Drift Cheaper
Why AI does not break product coherence by itself, but makes local UI deviation cheap enough that drift appears faster than many teams can review it.
Local Knowledge Beats Always-On Tools in AI-Assisted Development
Why the biggest gain in AI-assisted development comes from durable local knowledge and disciplined workflow structure, not from keeping every external tool always active.
The Problem Is Not Prompting. It Is Context Decay.
Why AI-assisted development breaks down when useful reasoning disappears between sessions, and why durable project context matters more than clever prompts.