November 19, 2024

Server-Side Rendering vs Static Site Generation in Next.js

Every Next.js project reaches the same fork in the road: for a given page, should the HTML be generated once at build time and served as a static file, or should the server construct it fresh on every incoming request? The distinction between Static Site Generation (SSG) and Server-Side Rendering (SSR) sounds like a low-level implementation detail, but it shapes how fast pages load, how stale the data can get, how much infrastructure a team needs to operate, and how smoothly a project scales under real traffic. Picking the wrong model does not just affect a benchmark — it can mean users see outdated content, origin servers buckle under load, or a codebase accumulates workarounds that compound over time.

The honest answer is that neither approach is universally superior, and Next.js deliberately supports both — along with hybrid patterns like Incremental Static Regeneration (ISR) — precisely because different pages within the same application have genuinely different requirements. A marketing landing page, a real-time stock ticker, and a user-specific dashboard each have distinct freshness, personalization, and performance constraints that make a single rendering strategy a poor fit for all three. This article works through the real tradeoffs so that the choice becomes a deliberate technical decision rather than a default.

What Is Server-Side Rendering in Next.js

Server-Side Rendering (SSR) in Next.js means that a page's HTML is generated on the server at the moment a user makes a request — not ahead of time during a build step, and not in the browser via JavaScript. Each incoming request triggers Next.js to run the page's data-fetching logic, construct the full HTML response, and send it back to the client. This stands in contrast to approaches where the server simply hands back a pre-built file that was generated earlier. In Next.js, SSR is enabled at the page level by exporting an async function called getServerSideProps. When Next.js detects this export, it knows to skip static generation for that page and instead run the function on every request.

Inside getServerSideProps, you have access to the full request context — including headers, cookies, and URL query parameters — which makes it possible to fetch personalised or time-sensitive data before the page reaches the user. The function returns a props object that Next.js passes to the page component, much like it would during static generation. The key difference is timing: with SSR, that data fetch happens on every single request, so the HTML delivered to the browser always reflects the current state of whatever data source you're querying.

The practical consequence of this model is that SSR pages are never stale, but they are also never free. Every request consumes server compute and adds latency relative to serving a pre-built file from a CDN edge node. For pages where the data changes frequently, depends on the authenticated user, or must reflect something that happened seconds ago, that tradeoff is often worth it. For pages where the content is stable across users and time, SSR introduces overhead without a meaningful benefit — which is exactly where static generation becomes the more appropriate default.

What Is Static Site Generation in Next.js

Static Site Generation (SSG) is a rendering strategy in which Next.js generates the full HTML for a page at build time — before any user ever makes a request. When you run next build, Next.js executes your data-fetching logic, renders the component tree into static HTML, and writes the result to disk. Those pre-built files are then deployed to a CDN or static hosting environment, where they can be served instantly without any server processing on each request.

The primary mechanism for SSG in Next.js is getStaticProps, an async function exported from a page file that runs only during the build. It can fetch from APIs, query a database, or read from the filesystem — anything that would normally happen server-side — but the result is baked into the HTML file rather than computed on demand. For pages with dynamic routes (such as blog posts or product pages), getStaticPaths works alongside getStaticProps to tell Next.js which URL paths to pre-render, so every variant of the page is produced as its own static file at build time.

The practical consequence is that SSG pages carry near-zero server load at runtime and tend to score well on performance metrics, since the browser receives a fully-formed HTML document directly from a CDN edge node. The tradeoff is staleness: the content reflects whatever was in the data source when the build ran, not necessarily what is there right now. Next.js offers Incremental Static Regeneration (ISR) as a middle-ground — pages can be revalidated in the background after a specified interval — but the core SSG pattern remains a build-time operation, making it best suited to content that changes infrequently or can tolerate a short lag between source updates and live pages.

Image

How SSR Works at Request Time

Every SSR page in Next.js follows the same runtime loop: the browser sends a request, Next.js intercepts it on the server, fetches the required data from an API or database, renders the full HTML, and returns it to the client. The page arrives pre-populated — no client-side data fetching required. The tradeoff is that this entire cycle must complete before the user sees anything, meaning each request adds measurable latency that scales with data source response times.

Image

How SSG Works at Build Time

During a static site generation build, Next.js calls data-fetching functions like getStaticProps once — at build time, not at request time. The resulting HTML files are written to disk and pushed to a CDN. When a user requests a page, the CDN serves a pre-rendered file directly, with no origin server involved and no per-request computation. The build pipeline is the bottleneck, not the server — which is why SSG pages load fast but reflect only the data that existed when the build ran.

SSR vs SSG: Key Attributes Compared

A side-by-side breakdown of SSR and SSG across the attributes that matter most when choosing a rendering strategy in Next.js.

Server-Side Rendering (SSR)Static Site Generation (SSG)
Data freshnessAlways up-to-date — fetched on every requestPotentially stale — data is fixed at build time
Time to first byte (TTFB)Slower — server must process each requestFaster — pre-built HTML is served immediately
Hosting requirementsRequires a Node.js server or serverless runtimeCan be hosted on any static CDN or file host
Build timeFast — no pre-rendering of pages requiredSlower at scale — every page is rendered at build time
ScalabilityScales with server capacity; higher infrastructure costScales easily via CDN; minimal origin load
Ideal use caseUser dashboards, real-time feeds, authenticated pagesMarketing sites, documentation, blogs, product listings

Performance and Core Web Vitals

One of the most measurable differences between SSG and SSR lies in Time to First Byte (TTFB) — the time it takes for the browser to receive the first byte of a page response. With SSG, the HTML is pre-built and stored on a CDN edge node geographically close to the user, so that first byte typically arrives in single-digit or low-double-digit milliseconds. With SSR, every request triggers a fresh render cycle on the server — fetching data, executing React components, and serializing the output — before anything is sent back to the browser, adding latency that compounds under load.

This TTFB gap has a direct downstream effect on Core Web Vitals, particularly Largest Contentful Paint (LCP) — the metric Google uses to measure how quickly the main content of a page becomes visible. Because LCP timing starts from the moment a navigation begins, a slower TTFB means a slower LCP almost by definition, regardless of how efficiently the client-side JavaScript runs afterward. For pages where search ranking and user retention both depend on perceived load speed, SSG has a structural advantage that SSR simply cannot close through runtime optimizations alone.

The important caveat is that this advantage only holds when the content can tolerate a degree of staleness. An SSG page reflects the state of the data at the time it was last built, so if the underlying content changes frequently, users may see outdated information until the next build runs and the CDN cache is invalidated. SSR, by contrast, guarantees freshness on every request — which is the right tradeoff for dashboards, personalized pages, or any content where correctness in real time outweighs the cost of the added latency. The performance question, then, is never purely technical: it requires understanding the acceptable staleness window for a given page.

Render fresh. Or render once.

When to Choose SSR

Server-side rendering earns its place when a page's content is fundamentally tied to the identity of the user making the request. Dashboards, account settings, order history pages, and personalized feeds all fall into this category: the correct response cannot be computed until the server knows who is asking and when they are asking. A statically generated version of such a page would either expose the wrong user's data or require so much client-side fetching that the SSG approach offers no real benefit — you'd just be doing SSR with extra steps.

Real-time data feeds are another clear-cut case for SSR. Stock tickers, live sports scores, inventory levels, and flight status boards must reflect the exact database state at the moment of each request. With SSG, the best a site can do is revalidate on some interval via Incremental Static Regeneration (ISR), but even a one-minute revalidation window is unacceptable for data that changes by the second. SSR eliminates that staleness entirely, because Next.js calls getServerSideProps fresh on every request, giving the response data that is accurate to within milliseconds of the page load.

A subtler but equally valid reason to reach for SSR is when the page's HTML must vary based on request-time signals that are invisible to a static build: cookies, authentication headers, geographic location resolved server-side, or A/B test assignments. These signals simply do not exist at build time, so SSG cannot incorporate them into the rendered output. SSR handles all of them naturally, because the server has full access to the incoming HTTP request before it renders a single line of markup.

When to Choose SSG

Static Site Generation is the right default for any content that doesn't change in response to who's requesting it or when. Marketing sites, company blogs, documentation portals, and product catalogs all fit this profile cleanly: the content is authored ahead of time, changes are planned and infrequent, and every visitor should see the same output. When Next.js pre-renders these pages at build time, the resulting HTML files can be distributed across a CDN and served in milliseconds — without a single Node.js process needing to wake up and do work per request.

The infrastructure cost argument for SSG is real and worth stating plainly. A statically generated site under heavy traffic imposes no additional compute burden — the CDN absorbs the load without scaling any application servers. This makes SSG particularly attractive for content with unpredictable traffic spikes, such as a blog post that gets picked up by a popular newsletter or a product page linked from a press article. The site doesn't need to be provisioned for worst-case concurrency because there's no runtime compute to provision.

SSG also pairs well with content that follows a predictable update schedule. If a documentation site ships new pages with each product release, or a news site refreshes its archive nightly, Incremental Static Regeneration (ISR) — Next.js's mechanism for revalidating pre-built pages on a time-based interval — keeps the static model intact while allowing content to stay reasonably current without full rebuilds. The key criterion is whether staleness is tolerable for minutes or hours: if the answer is yes, SSG with ISR almost always delivers better performance and simpler operations than a fully server-rendered alternative.

Image

ISR: The Middle Ground

Incremental Static Regeneration sits between SSG and SSR in a way that resolves the core tension between freshness and performance. Pages are served as static HTML — fast, cacheable, cheap to deliver — but Next.js revalidates them in the background on a configurable time interval. When the interval expires and a new request arrives, Next.js quietly rebuilds that page and swaps in the updated version. Teams get most of SSG's speed advantages without waiting for a full site rebuild every time content changes.

Common Mistakes to Avoid

The most widespread mistake teams make is defaulting to SSR for every route as a precautionary measure, reasoning that server-rendered HTML is always fresher and therefore always safer. In practice, this approach imposes unnecessary server load and increases response latency for pages whose content changes infrequently or not at all. A product listing page that updates once a day does not benefit from being regenerated on every request — it only suffers for it. Treating SSR as the universal fallback is a sign that the rendering decision was never made deliberately in the first place.

A closely related oversight is ignoring Incremental Static Regeneration (ISR) entirely. Many teams work as though Next.js offers only a binary choice between fully static and fully server-rendered, when ISR exists precisely to handle the middle ground: content that is mostly stable but does need to reflect updates within a predictable window. Pages built with ISR can be served from a CDN with near-static performance while still revalidating in the background at a defined interval. Skipping ISR forces developers into an unnecessary tradeoff that the framework was specifically designed to eliminate.

Finally, applications often accumulate inconsistent rendering strategies across routes without any documented rationale — one team member reaches for getServerSideProps, another uses getStaticProps, and a third leaves a route as a client-only shell, all without discussion. Over time, this produces a codebase where the rendering behavior of any given page is unpredictable and difficult to audit. The practical fix is straightforward: establish a brief decision record per route, even if it is just a comment explaining why that rendering mode was chosen. Explicit rationale is cheap to write and expensive to reconstruct six months later.

SSR and SSG are not competing philosophies — they are complementary rendering strategies that Next.js deliberately places within the same framework so teams can apply each where it actually makes sense. The practical decision comes down to two questions for every route: how frequently does the underlying data change, and how much traffic will that route absorb? Pages where content is stable and performance matters most belong to SSG; pages where data must reflect the current state of the world at request time belong to SSR. Next.js enforces no global choice — a single application can and often should use both, route by route, with Incremental Static Regeneration available as a middle path when neither extreme fits cleanly. Teams that internalize this model stop asking "which one should we use" and start asking the more productive question: "what does this specific route actually need?"