Back to blog
Development

Server Components and What Actually Changed in Web Architecture

It is not an API change, it is a change of mental model. Where code renders, where data lives, and why the client boundary matters more than any framework.

28 July 202612 min read

A Decade of SPAs, and the Return to the Server

For nearly ten years the default answer to any web project was the same: a JavaScript bundle that takes over in the browser, a client-side router, a state management layer, and an API serving JSON. The model worked. It also produced a legacy we pay for daily — hundreds of kilobytes of JavaScript shipped for pages that display text, waterfalls of fetches that only start after the runtime has loaded, and a time-to-interactive that cosmetic optimization can no longer rescue.

Server Components are not a new feature bolted onto that model. They are a course correction. The core idea is narrow and surprisingly radical: a component can render on the server, produce output, and never ship its JavaScript to the browser at all.

A Server Component is not a component that runs faster. It is a component whose code never reaches the user.

That distinction matters. Classic server-side rendering produced HTML on the server, then shipped the same code again in the bundle so it could hydrate. You paid twice. With Server Components, the code that formats a date, parses markdown, or maps a database row stays on the server. Only what genuinely needs interactivity enters the bundle.

The Mental Model: The Client Boundary

The only concept that truly matters is the boundary. The use client directive does not mark a component as client-side. It marks an entry point into the client graph — everything imported from there down ends up in the bundle.

That changes the strategy entirely. The typical mistake is putting the directive high up, in a layout or a page, because somewhere far below there is a dropdown that needs state. The result is that the whole subtree becomes client code, including purely presentational components, formatting libraries, and business logic that had no business being there.

The practical rule is the opposite:

  • Push the boundary as far down the tree as possible, toward the leaves.
  • Make the small component interactive, not the page that contains it.
  • A Server Component can pass a Client Component as children — that is how you keep an interactive shell without turning its content into client code.
  • Treat every use client as a measurable cost in kilobytes, not as a formality.

An accordion, a carousel, a copy-to-clipboard button — those are leaves. The page around them should not pay for them.

Data Moves Back Where It Belongs

The second structural shift is that data fetching becomes a server operation again. A component queries the database or the internal service directly, in the same place where it renders. The consequences are larger than they look:

  • The waterfall disappears. You no longer wait for the bundle, then hydration, then the first fetch, then a second fetch that depends on the first.
  • Key exposure disappears. You stop building public endpoints just so the browser can read something, and you stop shipping tokens to the client.
  • A pointless serialization layer disappears. You no longer turn a database row into JSON only to turn it back into an object three milliseconds later.
  • The payload becomes honest. You send the user the result, not the raw material plus the program that processes it.

On top of that come streaming and partial rendering. A page is no longer all-or-nothing: the fast parts arrive immediately, the slow ones stream in as they resolve, and the user sees structure and primary content without waiting on the slowest query. The effect on metrics is direct and measurable, and it ties closely to everything covered in the Core Web Vitals guide.

What Necessarily Stays on the Client

Server-first does not mean server-only. There is a short, clear list of things that cannot move:

  • Local state, and state shared across interactions.
  • Event handlers — click, input, drag, focus.
  • Animations and transitions driven by timelines or gestures.
  • Browser APIs: localStorage, geolocation, media devices, intersection observers.
  • Any library that assumes a window object exists.

The decision rule is simple: if the output depends on what the user does after load, it is client. If it depends only on data, it is server.

Caching Is the Hard Part

This is where teams break. Once rendering and data live on the server, performance no longer hinges on how small the bundle is, but on how well you understand what gets cached, where, and for how long. You simultaneously have a fetch cache, a route cache, a client-side router cache, and a CDN in front. Each has its own lifetime and its own invalidation rules.

The resulting bugs are not syntax errors. They are pages showing stale data in production and correct data in development. My advice, after several migrations: decide the revalidation strategy for each route explicitly before writing the component, not after. Editorial content and a live inventory dashboard differ in nature and cannot share one policy.

Hydration Mismatch: A Real Class of Bugs

When the server produces one HTML output and the client produces another, React complains — and sometimes simply throws away the server render and redoes everything, destroying exactly the advantage you migrated for. The causes are almost always the same:

  • Time-dependent renders: the current clock, a few minutes ago style calculations, a year computed at render time.
  • Random values: generated ids, shuffled lists, random placeholders.
  • Locale or timezone differences between server and browser.
  • Number and date formatting without an explicitly pinned locale.
  • Browser extensions that mutate the DOM before hydration.

The fixes are disciplined rather than clever: pin locale and timezone when formatting, compute volatile values in an effect that runs after mount, generate ids with the dedicated API, and treat any mismatch as an architectural bug rather than a warning you can live with.

When a SPA Is Still the Right Answer

Not everything being built is a content site. Entire categories still favour the client-first model:

  • Dashboards with dense filtering and complex in-memory state.
  • Editors — text, image, video, code — where every keystroke mutates a local model.
  • Realtime applications with persistent connections and millisecond-level updates.
  • Offline-first tools where the client owns the source of truth.

A store, an editorial platform, a marketing site, a portal with many indexable pages — server-first wins there decisively, and it wins on technical search visibility too. An internal application used eight hours a day by fifty logged-in people, carrying heavy state, can remain a SPA without any shame.

The Complexity Cost, Stated Honestly

The new model carries a real tax. You have two execution environments in one project, with different rules about what can be imported where. Error messages at the boundary are still rough. Debugging splits between the terminal and the browser console. And caching, as said, is a discipline of its own.

My decision criteria when evaluating a project:

  • Public content, many pages, SEO that matters, a team willing to learn: server-first, without hesitation.
  • Heavy state, few public pages, a small team and a short deadline: keep the SPA and optimize it.
  • An existing system that works and converts: migrate route by route, incrementally, starting with the pages people land on from search.

There is no prize for a full rewrite. There is only the measured outcome: how fast the user sees content, and how little code they had to download to see it. Everything else — including the way AI tooling changes development pace — is secondary to that architectural decision.

If you want to see that decision applied to a real project, let us discuss the right architecture for your case before the first line of code is written.

#react#next.js#arhitectura#rsc
質問

Frequently asked questions

No, and in most cases it would be a mistake. A healthy migration happens route by route: start with the public pages that receive search traffic, move them to server rendering, and measure the difference. The rest of the application can stay exactly as it is, and existing interactive components keep working once marked as client code. A full rewrite carries high risk and uncertain benefit.

Keep reading

Have a project that deserves this level of care?

I take one project at a time. If the timing works, let's talk.

Start a conversation
Scrie-mi pe WhatsApp