HomeBytesheetsRoadmapsUI TemplatesUI ToolsStack Discussion
5 Topics
Chapter•105 Minutes

Rendering Strategies: SSR, SSG, ISR, Dynamic and Streaming

Learn how Next.js decides when and where pages are rendered, including static generation, dynamic rendering, revalidation, streaming, and partial user experiences.

Static Rendering

Static rendering means content can be generated ahead of the request and reused. This is ideal for pages whose output does not need to change for every visitor.

Examples include marketing pages, documentation, public blog articles, and product catalogs that do not require per-request personalization.

Static output can be delivered efficiently through a CDN because the server does not need to execute application logic for every request.

Key Takeaways
  • Static rendering is excellent for stable public content.
  • Static pages can be distributed through caching infrastructure.
  • Static does not necessarily mean the source data can never change.

Dynamic Rendering

Dynamic rendering is appropriate when the response depends on request-specific information or data that must be resolved during the request.

Examples include personalized dashboards, authenticated pages, request-specific authorization, or content that must always be fresh.

Dynamic rendering should be used intentionally because it can increase server work and reduce caching opportunities.

Key Takeaways
  • Use dynamic rendering when request-specific information is required.
  • Personalization commonly requires dynamic execution.
  • Do not make every page dynamic without a reason.

Incremental Revalidation

Revalidation allows previously generated content to be reused while still allowing it to become fresh periodically or when explicitly invalidated.

Imagine a news homepage that can tolerate content being several seconds or minutes old. Revalidation can provide a useful balance between freshness and performance.

The key architectural question is not simply 'SSR or SSG?' but 'how fresh does this data actually need to be?'

Time-Based Revalidation
const response = await fetch('[https://example.com/api/products](https://example.com/api/products)', {
  next: {
    revalidate: 60
  }
});

const products = await response.json();
💡 The fetch configuration expresses that cached data can be revalidated periodically rather than requiring a fresh request every time.
Key Takeaways
  • Revalidation is useful when data changes but does not need request-level freshness.
  • Choose freshness requirements based on business needs.
  • Caching strategy should be part of application architecture.

Streaming and Suspense

Streaming allows a page to send useful UI to the browser before every piece of data is ready. React Suspense boundaries define areas that can show fallback content while slower server work completes.

A dashboard might render its navigation immediately while a slow analytics query continues. The user sees the shell first instead of waiting for the slowest operation.

Streaming is especially valuable when a page contains independent data sources with different response times.

Suspense Boundary
import { Suspense } from 'react';

export default function Dashboard() {
  return (
    <main>
      <h1>Dashboard</h1>

      <Suspense fallback={<p>Loading analytics...</p>}>
        <Analytics />
      </Suspense>
    </main>
  );
}
💡 The dashboard shell can be rendered while Analytics resolves independently.
Key Takeaways
  • Streaming improves perceived performance.
  • Suspense lets slow portions of a page load independently.
  • Do not create arbitrary Suspense boundaries; place them around meaningful slow or independent UI.

Route-Level Loading UI

A loading.tsx file provides a route-level loading experience. Next.js can use it while the corresponding route content is being prepared.

A good loading state should resemble the final page structure. Skeleton layouts are often better than generic spinners because they reduce layout shifts and communicate what is loading.

Loading UI
export default function Loading() {
  return (
    <div aria-busy='true'>
      <div className='skeleton skeleton-title' />
      <div className='skeleton skeleton-card' />
      <div className='skeleton skeleton-card' />
    </div>
  );
}
💡 A route-level loading component provides immediate feedback while server-rendered content is being prepared.
Key Takeaways
  • Loading states are part of application UX.
  • Skeletons can communicate structure better than generic spinners.
  • Design loading states around realistic slow paths.