Not hype — a practical look at where Server Components help, where they don't, and how to think about the boundary between server and client.
React Server Components landed in Next.js App Router as a default, which means most developers encountered them not by choosing to adopt a new pattern but by having their assumptions about how React works quietly invalidated. Components that import server-only modules and don't bundle them to the client. Components that run on the server and never appear in the JavaScript bundle. A different mental model for what a component is and where it runs.
The core idea is that not every component needs to be interactive, and not every component needs to send code to the browser. If a component fetches data and renders HTML, and nothing about it changes based on user interaction, there's no reason for it to run on the client at all. Server Components make that distinction explicit and structural rather than a performance optimisation you apply after the fact.
The practical benefit that matters most for most applications is bundle size. Heavy dependencies — markdown parsers, date libraries, syntax highlighters, data transformation utilities — can live entirely on the server. They fetch data, render output, and produce HTML. None of that code ships to the browser. For content-heavy applications, the difference in JavaScript payload can be significant, and the improvement in initial load performance is proportional.
The second benefit is data access simplicity. In a Server Component, you can query a database or call an API directly, without creating an API route to proxy the request and a client-side fetch to call it. The data fetching and the rendering happen in the same function. For many common patterns — a product page that needs a database record, a blog post that needs content from a CMS — this is meaningfully simpler than the equivalent pattern in the pages router.
The part that trips people up is the boundary. A Server Component can render Client Components, but a Client Component cannot render a Server Component as a child (it can render one as a prop passed from a server parent, but that's a subtler pattern). State, event handlers, browser APIs, and hooks all require client components. The architecture decision is where to draw the line: push as much as possible to the server, use client components only where you need interactivity.
The honest version of the tradeoff is this: Server Components make the common case — fetch data, render HTML — simpler and faster, at the cost of making the mental model more complex. You need to understand where each component runs, which libraries work in which context, and how to compose server and client components correctly. For teams building data-heavy, content-rich applications, the investment pays off quickly. For applications that are primarily interactive — dashboards, editors, real-time tools — the benefit is smaller because most of the components need to be client components anyway.
The most useful thing is to stop thinking of "server" and "client" as deployment concepts and start thinking of them as rendering contexts. Server Components render once, on the server, and produce HTML. Client Components render on the client and can respond to state changes. Most applications need both, in the right proportion.
Stay in the loop
Get new posts delivered to your inbox
Platform engineering, frontend craft, and systems thinking. No spam.