Server-Side Rendering With Nuxt: When SSR Beats SPA
A practical breakdown of when to use SSR, SSG, ISR, or SPA in Nuxt — with real performance data and architectural trade-offs from production deployments.

James Ross Jr.
Strategic Systems Architect & Enterprise Software Developer
One of the most consequential architectural decisions on a web project is choosing your rendering strategy. Get it wrong and you are fighting your framework on every performance problem. Get it right and performance falls out naturally from your architecture.
Nuxt gives you four rendering modes: server-side rendering (SSR), static site generation (SSG), incremental static regeneration (ISR), and client-side rendering (SPA). Each has a genuine use case. The mistake is defaulting to SSR for everything or, worse, defaulting to SPA because that is what you are most familiar with.
I am going to walk through each mode, when to use it, and the real trade-offs — including some data from production applications.
Understanding What SSR Actually Does
When a user requests a page from an SSR application, your server runs your Vue components, generates the HTML, sends it to the browser, then the browser downloads JavaScript and "hydrates" the page so it becomes interactive. The user sees content immediately on first load instead of staring at a blank screen while JavaScript downloads and runs.
The full sequence:
- Browser requests
/products/123 - Server renders the Vue component with real data
- Server sends complete HTML
- Browser renders HTML immediately
- Browser downloads JavaScript
- Vue hydrates the existing HTML
- Page is now interactive
The SPA equivalent of step 3 sends an empty HTML shell. The user sees nothing until steps 5 and 6 complete. On a fast connection with a small app, the difference is negligible. On a mobile connection with a large app, it is the difference between content appearing in 0.8 seconds versus 4 seconds.
When SSR Genuinely Wins
E-commerce product pages. Organic search traffic converts. Bots cannot reliably execute JavaScript. If your product pages need to rank and you have dynamic inventory, pricing, and product details, SSR is the answer. The SEO benefit alone justifies the server costs for most e-commerce businesses.
News and editorial content that is personalized. A news site might show different content based on location or subscription status. SSG cannot handle personalization. SPA shows the wrong content until JavaScript loads. SSR can make the decision on the server and send the right HTML the first time.
Authenticated dashboards with data that changes frequently. If your dashboard data changes every few minutes, static generation is not useful. SSR lets you render with fresh data on every request without the SPA's blank-screen problem.
I rebuilt a SaaS dashboard from an SPA to SSR last year. First meaningful paint improved by 1.8 seconds on median mobile hardware. Support tickets about "the app taking forever to load" dropped by 70% in the month after launch. Those numbers make the business case for SSR better than any benchmark.
When SSG Beats SSR
Marketing sites and landing pages. The content rarely changes. There are no authenticated states. You want the fastest possible performance. Static generation wins every time. Pre-built HTML served from a CDN is faster than any server can respond, because there is no server involved.
Documentation sites. Content updates happen through Git merges. You want global performance. Nuxt generates hundreds of pages at build time, Cloudflare Pages serves them from the edge, and your users get sub-100ms response times globally.
Blogs. Unless you have thousands of articles and need incremental builds, just generate everything statically. The build takes longer but the runtime experience is unmatched.
The trade-off with SSG is build time and rebuild frequency. Adding one blog post to a site with 2,000 pages means rebuilding all 2,000 pages. For most sites this takes a few minutes and happens rarely enough that it is not a problem. For sites where content editors expect new pages to appear in seconds, SSG is the wrong choice.
Incremental Static Regeneration: The Middle Ground
ISR (Nuxt calls it "hybrid rendering") lets you specify a revalidation time per route. The page is generated statically but refreshed in the background at your specified interval:
// nuxt.config.ts
routeRules: {
'/products/**': { swr: 3600 }, // Regenerate hourly
'/blog/**': { prerender: true }, // Static, rebuild on deploy
'/dashboard/**': { ssr: true }, // Always SSR
'/api/**': { cors: true }, // API routes, no caching
}
This is powerful for content that changes occasionally but not every request. Product pages can regenerate hourly. A user sees cached HTML that is at most one hour old — much better than SPA, close to SSR quality, at a fraction of the server cost.
The stale-while-revalidate pattern means users never wait for regeneration. They get the cached version immediately. The new version generates in the background and is available for the next request.
SPA Mode: When It Is Actually Right
SPA is not a worst-case fallback — it is the right choice for specific application types.
Authenticated apps with no SEO requirements. If your app requires login to access any page, search engines cannot index it anyway. SSR adds server cost without SEO benefit. Build an SPA.
Highly interactive applications. Rich text editors, design tools, spreadsheet-like interfaces — these are application states that make no sense to server-render. They need the full JavaScript environment immediately and do not have meaningful SEO surface area.
Internal tooling. If your users are on a corporate network with fast connections and you have no external traffic, the SPA performance trade-off is acceptable. Do not add server infrastructure complexity for an internal dashboard used by 50 people.
Hydration: The Hidden Footgun
SSR introduces a class of bugs that SPA developers have never encountered: hydration mismatches. When the server renders HTML that does not match what the client would render, Vue logs a hydration warning and re-renders the component client-side. In severe cases this causes layout flashes or broken component state.
Common causes:
<!-- BAD: Math.random() produces different values on server and client -->
<template>
<div :id="`item-${Math.random()}`">...</div>
</template>
<!-- BAD: new Date() produces different timestamps -->
<template>
<span>{{ new Date().toLocaleDateString() }}</span>
</template>
The fix for random IDs is to use Vue's useId() composable. The fix for dates is to format them consistently, or use <ClientOnly> to defer rendering to the client.
Anything that depends on browser APIs (window, localStorage, navigator) will break on the server. Wrap these with if (process.client) checks or the <ClientOnly> component:
<ClientOnly>
<MapComponent />
<template #fallback>
<div class="h-64 bg-gray-100 animate-pulse rounded" />
</template>
</ClientOnly>
The #fallback slot renders on the server and during hydration — use it to show a skeleton or placeholder that matches the component's dimensions to prevent layout shift.
Performance Metrics That Guide the Decision
Here are the numbers I look at when making rendering strategy recommendations:
Time to First Byte (TTFB): SSG wins (CDN edge delivery), ISR is close (cached responses), SSR is slowest (server must run).
First Contentful Paint (FCP): SSR and SSG roughly equal (both send HTML). SPA is slowest.
Largest Contentful Paint (LCP): SSG usually wins. SSR depends on server response time. SPA depends on API response time after JS loads.
Server costs: SSG lowest (static files, no compute), ISR middle (compute only on regeneration), SSR highest (compute every request).
For most projects, the right answer is hybrid: static generation for marketing and content, SSR for authenticated routes that need fresh data, and SPA for highly interactive tools. Nuxt's routeRules makes this configuration straightforward.
Do not default to SSR without thinking through the trade-offs. And do not default to SPA because it is simpler to reason about. The rendering strategy should follow from your content, your users, and your SEO requirements.
If you are designing a new Nuxt application and want help choosing the right rendering strategy for your specific requirements, book a call: calendly.com/jamesrossjr.