Back to Blog
Web Development

React Server Components: 8 Months In. The Good, The Bad, The Ugly

Zyptr Admin
22 April 2024
9 min read

The Good: Bundle Size Actually Dropped

Let's start positive. The promise of React Server Components is that you send less JavaScript to the client. And for our projects, this has genuinely delivered. Our largest client dashboard — a SaaS analytics product — saw its JavaScript bundle drop from 380KB gzipped to 140KB gzipped after the RSC migration. That's a 63% reduction. The initial page load went from 3.2 seconds to 1.4 seconds on a 4G connection. These aren't synthetic benchmarks — these are real Lighthouse runs on production.

The reason is straightforward: most of the dashboard is tables, charts, and data displays. With RSC, all the data fetching and rendering logic stays on the server. The client only gets the interactive parts — filters, dropdowns, date pickers. For data-heavy applications, this architecture is genuinely transformative.

The Good: Data Fetching Is Simpler

No more useEffect-fetch-setState-loading-error dance. No more React Query (sorry, TanStack Query). In Server Components, you just await your data and render. It feels like writing PHP, and honestly, that's a compliment. The code is more readable, there are fewer state management bugs, and junior developers on our team picked it up faster than the old client-side fetching patterns.

We've also found that the "waterfall" problem everyone warns about is mostly solved with parallel data fetching. Put your independent fetches in a Promise.all, use Suspense boundaries for progressive loading, and you get excellent performance without the complexity of a client-side caching layer.

The Bad: The Mental Model Is Confusing

Even after eight months, our team still occasionally puts a useState inside a Server Component and spends ten minutes confused by the error message. The 'use client' boundary is clear in theory but messy in practice. Real components often need both server-side data and client-side interactivity. You end up splitting what was one component into two: a server wrapper that fetches data and a client child that handles interaction. It's not terrible, but it adds cognitive overhead and file count.

The rules about what you can and can't pass across the server/client boundary are also subtle. You can pass serializable props (strings, numbers, objects) but not functions, classes, or React elements created on the server. We've had multiple instances where a component worked in development but failed in production because of serialization issues that didn't surface during SSR in dev mode.

The Bad: Third-Party Libraries Are a Landmine

If a library uses useState, useEffect, or any browser API internally, it can't be used in a Server Component. Sounds obvious, but the ecosystem hasn't fully adapted yet. We've had to fork or wrap libraries that assume client-side rendering. Date pickers, rich text editors, charting libraries — many popular options don't work as Server Components and need to be wrapped in client component boundaries. Each wrapper adds complexity and potential for bugs.

The Ugly: Caching and Revalidation

Next.js's caching behavior with RSC has been, frankly, a mess. The aggressive default caching in Next.js 14 caused bugs in three of our four projects where users saw stale data after mutations. We've had to add revalidatePath and revalidateTag calls throughout our code, and even then, the behavior isn't always predictable. The Next.js team acknowledged this and made caching opt-in by default in Next.js 15, but we're still on 14 for most projects.

The fetch cache, the full route cache, the router cache — there are too many caching layers, each with different invalidation rules. Our team maintains an internal wiki page just documenting which cache does what and how to bust each one. That shouldn't be necessary.

Our Verdict

We're continuing to use RSC for new projects. The performance benefits are real and the data fetching model is genuinely better. But we budget more time for RSC projects because of the ecosystem friction, and we've standardized patterns for common pitfalls. It's good technology with rough edges, and the edges are getting smoother with each Next.js release. Just don't expect it to be simple.

reactserver-componentsnextjsperformance
Let's Work Together

Have a Project in Mind?
Great?

Let's talk about building your next product.