Brand logoobin
ProjectsInsightsContact
Resume

2020 © Shahadat Robin

Choosing the Right Rendering Strategy: SSR vs Streaming SSR vs CSR

Choosing the Right Rendering Strategy: SSR vs Streaming SSR vs CSR
Author Shahadat Robin's Image

Shahadat Robin

Software Developer, LemonHive

a month ago

If you work with modern React or Next.js apps long enough, you eventually hit the same question:

Should this page be server-rendered, streamed, or client-rendered?

At first, it feels like a purely technical choice. Later, you realize it affects almost everything:

  • perceived performance
  • SEO
  • caching strategy
  • infrastructure cost
  • complexity of state management
  • user experience during navigation

I used to think the answer was "SSR is better because the server sends HTML first." In practice, that is only partly true. A rendering strategy is not something we choose for the framework. It is something we choose for the behavior of a specific page.

This article is my practical mental model for deciding when to use:

  • SSR
  • Streaming SSR
  • CSR
  • a Hybrid approach

My goal here is not to explain the textbook definitions only. I want to document the decision process that actually helps during real product work.

The Short Version

If you only want the summary:

  • Use `SSR` for public pages, SEO-heavy pages, and pages where initial HTML matters.
  • Use `Streaming SSR` for pages with a useful shell and slower independent sections.
  • Use `CSR` for authenticated, interactive, frequently revisited app screens.
  • Use `Hybrid` for dashboards: server-render the shell, but let the main data panels be client-owned and cached.

That is the simplified answer. The rest of this article explains why.

First Principle: Rendering Is a Product Decision

When we choose a rendering method, we are really answering these questions:

  • Does the initial HTML matter?
  • Does search engine indexing matter?
  • Is the page mostly read-only or interaction-heavy?
  • Will users revisit it repeatedly in the same session?
  • Should the cache live on the server or in the browser?
  • Is this page entered directly from outside, or mostly through in-app navigation?

This is why two pages in the same app may deserve completely different strategies.

For example:

  • a public landing page and
  • an authenticated members-management page

should usually not be optimized the same way.

SSR: Server-Side Rendering

In SSR, the server renders the page for the request and sends HTML to the browser.

When SSR is a good fit

SSR is most useful when:

  • The page is public
  • SEO matters
  • The page should be shareable with meaningful content
  • First-load correctness matters more than revisit speed- the content is mostly read-heavy

Examples:

  • Marketing pages
  • Product detail pages
  • Public blog posts- pricing pages
  • Documentation pages
  • Public report pages

Why SSR helps

SSR gives you:

  • Meaningful HTML on first response- good SEO support- better social sharing previews
  • Better first impression for pages entered from external sources

Where SSR becomes weak

SSR starts to lose value when:

  • The page is behind authentication
  • The page is mostly interactive
  • Users bounce around the same route many times
  • The same data is better reused from client cache
  • The server has to repeat expensive work on every navigation

That is the point where teams often feel "the page technically works, but it still feels slow."

Streaming SSR

Streaming SSR is still server rendering, but instead of waiting for the full page, the server can send parts of the UI gradually.

That means the user can see:

  • The page shell first- slower sections later- skeletons or placeholders in between

When Streaming SSR is a good fit

Streaming SSR is best when:

  • The page has a meaningful layout even before all data is ready- some sections are fast and others are slow
  • The slow sections are independent- the page still benefits from server rendering

Examples:

  • Analytics overview pages- dashboards with multiple independent widgets
  • Detail pages with summary + related content + recommendations- pages where the header or hero can render before deep data panels

Why Streaming SSR helps

It improves perceived performance because the user gets visual progress earlier.

The key phrase here is meaningful progress.

If the shell appears quickly and the user understands the page is loading section by section, streaming feels great.

Where Streaming SSR becomes weak

Streaming SSR is not always a win. It becomes less useful when:

  • The whole page depends on one main slow fetch
  • Every navigation causes the same fallback flash- the page is mostly internal and revisited often
  • The skeleton is shown so often that it becomes annoying instead of helpful

This is a common trap in authenticated dashboards. We add Suspense, see a nice loading skeleton, and assume the architecture is now optimized. But if the page re-suspends every time the user comes back, the UX can still feel worse than a cached client-rendered screen.

CSR: Client-Side Rendering

In CSR, the browser loads the JavaScript, fetches the data, and renders the page or the main content on the client.


When CSR is a good fit

CSR is usually the best choice when the page is:

  • Authenticated- highly interactive
  • Revisited often
  • Not SEO-sensitive
  • Driven by filters, sorting, pagination, selection, inline edits, or mutations

Examples:

  • Members management
  • Settings pages
  • Notifications center
  • Project lists
  • Admin tables
  • Dashboards that behave like applications rather than documents

### Why CSR helps

CSR lets you keep the data close to the user session.
That means:

  • Fast revisits
  • Warm cache between navigations
  • Smoother updates after mutations
  • Less full-page fallback flashing

This is especially useful when using client-side data libraries like Apollo Client, React Query, or SWR.

Where CSR becomes weak

CSR is not ideal when:

  • SEO matters
  • The user often lands directly from search or social links
  • The initial blank/loading state is too expensive
  • The app depends too much on JavaScript before showing anything useful

So CSR is not "bad." It is simply best when the page behaves like an application surface rather than a public document.

Hybrid Rendering

In real-world apps, the best answer is often not pure SSR or pure CSR.
A hybrid approach usually works better:

  • Render the shell on the server
  • Move the main data panel to the client
  • Cache the data in the browser by route params

This is usually the sweet spot for authenticated dashboards.

Why hybrid is often the best dashboard strategy

With a hybrid model:

  • The user sees the page layout quickly
  • Metadata and stable layout still come from the server
  • The interactive content uses client cache
  • Repeat navigation feels much faster
  • Mutations become easier to manage

For example:

  • Server-render the page title, breadcrumbs, and shell
  • Client-render the members table with cache keyed by workspaceID

That way, changing the workspace fetches fresh data, but revisiting the same workspace does not feel like a first load every time.

The Most Important Question: Where Should the Cache Live?

This is the question that changed how I think about rendering.
Many rendering decisions are actually cache decisions.

Server-side cache is better when

  • Many users benefit from the same precomputed result
  • The content is safe to reuse broadly
  • The page is mostly read-only
  • SEO and first-request performance matter

Client-side cache is better when

  • The data is user-specific
  • The page is part of a logged-in app
  • The user frequently revisits the same route
  • The screen is interactive and mutation-heavy
  • Route params naturally key the data

Examples of great client cache keys:

  • workspaceID
  • projectID
  • urlID
  • workspaceID + filters

Once I started thinking in terms of cache ownership, rendering choices became much easier.

My Practical Decision Framework

When I evaluate a page now, I go through this checklist.

1. Does this page need SEO?

If yes, I start with `SSR` or `Streaming SSR`.
If no, I move quickly toward `CSR` or `Hybrid`.

2. Is this page mostly for logged-in users?

If yes, SSR becomes less valuable by default.
Not useless, but less valuable.

3. Is the page mostly read-only or interaction-heavy?

If it is read-heavy, server rendering is often helpful.
If it is interaction-heavy, client ownership is often better.

4. Will users revisit it often in one session?

If yes, client cache usually matters more than perfect first-load HTML.

5. Does the page have multiple independent slow sections?

If yes, `Streaming SSR` may be a strong fit.
If not, a giant streamed boundary may add complexity without much benefit.

6. Does the page need to feel like an app or like a document?

This may sound simple, but it is one of the best heuristics.

  • If it should feel like a document, favor server rendering.
  • If it should feel like an app, favor client cache and interactivity.

A Simple Route Classification

Here is the mental map I now use.

Best candidates for SSR

  • landing pages
  • Marketing pages
  • Pricing pages
  • Blog posts
  • Docs pages
  • Public reports
  • Product pages

Best candidates for Streaming SSR

  • Analytics overview pages
  • Detail pages with multiple slow sections
  • Pages with useful shell + slower panels- pages entered directly where initial structure matters

### Best candidates for CSR

  • Members management
  • Settings pages
  • Admin tables
  • Notification centers
  • List pages with filtering/sorting
  • Mutation-heavy dashboard routes

Best candidates for Hybrid

  • Authenticated dashboards
  • Workspace pages
  • Project lists
  • Account centers
  • Internal reporting tools with stable shell + interactive panels

Common Mistakes

Over time, I have noticed a few patterns that create unnecessary complexity.

Mistake 1: Assuming SSR is always faster

SSR can improve first response quality, but it can also cause repeated server work and repeated loading fallbacks during navigation.

SSR is not automatically the fastest user experience.

Mistake 2: Using Streaming SSR everywhere because `Suspense` looks modern

Streaming is useful when it creates meaningful progress.
If it only causes frequent skeleton flashes on internal routes, it may be the wrong choice.

Mistake 3: Forcing dynamic rendering too high in the route tree

If a parent layout is marked dynamic too aggressively, many children lose optimization opportunities even when they do not need to.
This is easy to miss and can affect the whole app.

Mistake 4: Using broad cache invalidation

A tag like `workspace-members` is often too broad. A better tag is - `workspace-members:${workspaceID}`

Narrow invalidation makes refresh behavior more predictable and efficient.

Mistake 5: Treating every dashboard page like a public page

Authenticated app screens should be optimized for:

  • Revisit speed
  • Local cache reuse
  • Smooth mutation flow

not only for initial HTML generation.

What I Would Choose in Practice

If I am building a modern SaaS app, my default approach would be:

Public pages

  • `SSR`

Content-heavy detail pages

-`Streaming SSR`

Authenticated management screens

-`Hybrid`

Mutation-heavy, frequently revisited panels

-`CSR` or `Hybrid` with strong client cache

That keeps the architecture practical instead of ideological.

Example Thought Process

Let us say I am deciding how to render a workspace members page.
Questions:

  • Is it public? No.
  • Does SEO matter? No.
  • Is it interactive? Yes.
  • Will users revisit it often? Yes.
  • Does it benefit from client cache keyed by `workspaceID`? Absolutely.

My answer:

  • Not full SSR
  • Not full page streaming as the main strategy
  • Use a hybrid model

That means:

  • Server-render the shell
  • Client-render the members data
  • Cache by `workspaceID`
  • Update the cache after add/remove/change-role actions

This gives better revisit performance and a more app-like feel.

Final Rule of Thumb

If I had to compress everything into one rule, it would be this:

Choose rendering based on how the page is used, not based on what the framework makes easy.

And if I had to compress it one more time:

  • Public and discoverable: `SSR`
  • Structured with slow independent sections: `Streaming SSR`
  • Interactive and revisited: `CSR`
  • Dashboard shell plus cached panels: `Hybrid`

Closing Thoughts

For a long time, I looked at rendering strategies mainly as technical options. Now I see them as UX and caching strategies.

The right choice is usually the one that answers these two questions well:

  • What does the user need to see first?
  • Where should this data stay warm?

If you answer those two questions honestly, the rendering strategy becomes much clearer.

That is the model I want to keep using in future projects, and the reason I wrote this down for myself.

Related

NextJSSanity

Learn how to embed Sanity (v3) Studio inside Next.js project

NextJSStater

How I Setup Next.js Repository for High-Velocity Projects

NextJSSentry

Sentry in Next.js