Solvantis Group Logo

Server Components Made Me Rethink What 'Fast' Even Means

Shaving milliseconds off a client bundle used to feel like winning. Then a client's dashboard shipped 0KB of JS for the initial paint, and the whole mental model I'd been using for four years stopped making sense.

Solvantis Engineering TeamAugust 10, 20267 min read
#Next.js#React Server Components#Web Performance#App Router

For most of the last four years, "fast" meant one thing to me: get the bundle smaller. Code-split harder. Lazy-load the modal nobody opens on first visit. Tree-shake whatever lodash function someone imported for one `.get()` call. It worked, mostly, and it made me feel like I understood performance.

Then we rebuilt an admin dashboard for a client on the Next.js 15 App Router, and the initial JS payload for the main table view came out to zero kilobytes. Not "small." Zero. And I had to sit with that for a minute, because my entire performance vocabulary was built around minimizing a number that had just stopped existing for that route.

Section Overview

What was actually going on

Server Components aren't a bundling trick. They're a different question entirely. Instead of "how do I ship less JavaScript," the question becomes "does this component need JavaScript at all." A table that renders 200 rows of account data, does some formatting, and sorts server-side before it ever reaches the browser doesn't need React running client-side to exist. It needs HTML. The browser just paints it.

Where it gets interesting — and where I've watched three different engineers on our team get it wrong before it clicked — is the boundary problem. The moment you need `onClick`, `useState`, or anything that reacts to a user's mouse, you've crossed into Client Component territory, and everything below that boundary in the tree ships JS again. So the actual skill isn't "use Server Components." It's figuring out where the boundary should sit.

On that dashboard, the mistake we almost made was wrapping the whole data table in a client component because one column had an inline "mark as reviewed" toggle. Do that and you've client-shipped 200 rows of rendering logic to support one button. What we did instead: server-render the table itself, and drop a tiny client island — just the toggle button, nothing else — into each row. The table stays server-rendered. Only the interactive sliver ships JS.

Section Overview

The part nobody mentions in the docs

Data fetching waterfalls get worse before they get better with this model, not automatically better. If you're not careful, a server component tree with three levels of nested `await fetch()` calls will serialize sequentially on the server the same way client-side waterfalls used to serialize in the browser — except now you can't see it in the network tab, because it's happening server-side before any HTML goes out. We didn't catch this until we put actual server-side timing logs in and saw a 340ms "hang" before first byte on one route. The fix was parallelizing the fetches with `Promise.all` at the layout level instead of letting each nested component fetch independently — obvious in hindsight, invisible until you go looking for it.

Section Overview

Where I've landed on this

I'm not going to tell you Server Components make everything faster by default, because they don't — they make it *possible* to ship less, and then it's on you to actually draw the boundaries correctly. A badly-boundaried Server Component app can still ship a bloated client bundle; it just hides the bloat in a different place than it used to.

What changed for me isn't a number. It's that "fast" used to mean a smaller version of the same thing. Now it sometimes means the thing doesn't need to exist client-side at all, and the interesting engineering work is figuring out which parts of a page actually need to.

If you're evaluating a rebuild and someone tells you "we'll just move it to Next.js and it'll be faster," ask them where they're planning to draw the server/client boundary. If they don't have an answer yet, that's fine — most people don't until they've drawn it wrong once.

Execute This Architecture With Solvantis

Ready to put these engineering patterns into production? Explore our custom application packages or view live production case studies.

ENGINEERING CONSULTATION

Discuss Your Architecture & Scope

Talk directly with senior engineers about your product roadmap, database models, or web performance audits.