When building a Next.js application, one of the biggest questions is:
Where should your UI be rendered?
On the browser, the server, or both?
Understanding the evolution of rendering helps you choose the right approach for performance, SEO, and user experience.
1️⃣ Client-Side Rendering (CSR)
With CSR, the browser receives a mostly empty HTML file along with a JavaScript bundle. React then builds the UI in the browser.
✅ Advantages
Rich interactivity
Great for highly dynamic applications
❌ Drawbacks
Poor SEO because the initial HTML contains little content
Slower first page load
Larger JavaScript bundles
Users often see loading spinners before content appears
2️⃣ Server-Side Rendering (SSR)
SSR generates HTML on the server for every request and sends fully rendered HTML to the browser.
✅ Benefits
Better SEO
Faster initial content
Improved performance on slower devices
After the HTML arrives, React performs Hydration—attaching JavaScript so the page becomes interactive.
3️⃣ The Problems with Traditional SSR
Although SSR improved many things, it introduced an "all-or-nothing waterfall" problem.
Before users can interact:
❌ All data must be fetched
⬇️
❌ Entire HTML must be rendered
⬇️
❌ Entire JavaScript bundle must load
⬇️
❌ Entire page must hydrate
Only then can users interact with the page.
If one section is slow, everything else waits.
4️⃣ Suspense + Streaming SSR
React introduced Suspense to solve these bottlenecks.
It enables:
✅ HTML Streaming
Instead of waiting for the entire page, the server streams completed sections as soon as they're ready.
Users can start seeing content much earlier.
✅ Selective Hydration
React no longer waits to hydrate the whole page.
Interactive sections become usable as soon as their JavaScript is available.
Even better, React prioritizes hydration based on user interaction.
If a user clicks a component before another finishes hydrating, React immediately hydrates the clicked component first.
5️⃣ But Suspense Still Isn't Perfect
Even with streaming:
Users still download the entire JavaScript bundle eventually.
Static components still get hydrated unnecessarily.
Client devices still perform a significant amount of JavaScript work.
This raises an important question:
Should every component really be sent to the browser?
6️⃣ React Server Components (RSC)
React answered this with React Server Components.
Instead of sending everything to the browser, React splits components into two categories:
🖥️ Server Components
Run only on the server
Never shipped to the browser
Can access databases and APIs directly
No hydration required
Smaller JavaScript bundles
Better security
Faster rendering
💻 Client Components
Handle interactivity
Use state, effects, and event listeners
Access browser APIs
Can still be server-rendered initially for better performance
🎯 Why RSC Matters
React Server Components provide:
✅ Smaller bundle sizes
✅ Faster page loads
✅ Better SEO
✅ Improved security
✅ Efficient data fetching
✅ Built-in caching
✅ Streaming support
✅ Less JavaScript sent to users
The result is a faster, more scalable application while keeping interactive experiences where they're actually needed.
Final Thoughts
Rendering has evolved like this:
CSR → SSR → Suspense + Streaming → React Server Components
Each step solves limitations of the previous one, making modern React applications faster and more efficient.
If you're using the Next.js App Router, you're already building on top of the React Server Components architecture.
Understanding why these rendering strategies evolved—not just how they work—makes it much easier to build performant web applications.
💬 Which rendering concept was the most difficult for you to understand when learning Next.js?
Top comments (1)
When we first started migrating some key dashboards to the App Router, the biggest mental hurdle wasn't just how to use RSCs, but truly understanding where their benefits outweighed the added architectural complexity. For us, the sweet spot quickly became pages or large, data-driven sections that are mostly static post-render, or only need minimal interactivity at the leaf nodes. Think reporting views, user profiles, or configuration pages that pull a lot of data initially and then maybe have a few toggles or input fields. The initial page load performance gains from smaller client bundles are undeniable there.
Where it gets tricky is when you push a fully interactive component, like a complex data table with filtering, sorting, and inline editing, too far up the server component tree. The hydration penalty and the need to pass state boundaries correctly can quickly negate those initial server-side benefits, sometimes leading to a more convoluted component architecture than a well-optimized client component ever would. We've found a good heuristic is to keep the interactive 'islands' as client components, often co-located with their server-side data fetching parents using patterns like passing serializable props or making direct client-side API calls for subsequent interactions.
For a SaaS product, managing data fetching at scale across these paradigms is paramount. Avoiding waterfalls and ensuring efficient database queries, whether directly from RSCs or via a dedicated API layer for client components, becomes a constant optimization task. The ability to stream parts of the UI with Suspense is powerful for perceived performance, especially on slower connections or for components that take a moment to resolve their data. It's a powerful toolkit, but like any powerful tool, understanding its sharp edges and optimal use cases is key to unlocking its real value without over-engineering.