Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Nextjs
  4. /Static Dynamic Rendering
Your Progress0%
0 of 70 completed

NextJS Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication

Understanding Static and Dynamic Rendering

When and how Next.js renders your pages

Next.js renders pages in different ways to optimize performance. Static Rendering (SSG) pre-generates HTML at build time for lightning-fast loads. Dynamic Rendering (SSR) generates HTML on each request for personalized content. Incremental Static Regeneration (ISR) combines bothβ€”static with periodic updates. Understanding when and how pages render is crucial for building fast, scalable applications!

Three Types of Rendering

1. Static Rendering (SSG - Static Site Generation)

  • When: Build time
  • Caching: Served from CDN indefinitely
  • Use for: Marketing pages, blog posts, documentation
  • Speed: ⚑ Fastest (pre-rendered HTML)

2. Dynamic Rendering (SSR - Server-Side Rendering)

  • When: Request time (every request)
  • Caching: No caching (always fresh)
  • Use for: User dashboards, personalized content, real-time data
  • Speed: 🐒 Slower (rendered on demand)

3. Incremental Static Regeneration (ISR)

  • When: Build time + periodic revalidation
  • Caching: Cached with time-based revalidation
  • Use for: E-commerce products, news articles, CMS content
  • Speed: ⚑ Fast (static) + πŸ”„ Updated (revalidated)

Next.js Default Behavior

Next.js defaults to Static Rendering whenever possible for maximum performance. A route automatically becomes dynamic if it uses dynamic functions (cookies, headers, searchParams) or opts out of caching.

Static Rendering (SSG)

Fully Static Page

app/about/page.tsx
// This page is FULLY STATIC
export default function AboutPage() {
  return (
    <div>
      <h1>About Us</h1>
      <p>We are a company that builds amazing things.</p>
      <p>Founded in 2024.</p>
    </div>
  );
}

// βœ… Rendered at build time
// βœ… HTML cached indefinitely
// βœ… Served from CDN
// βœ… Fastest possible load time
// βœ… No server processing on request

Static with Cached Data

app/blog/page.tsx
// Static page with data fetching
export default async function BlogPage() {
  // Default: cached indefinitely
  const posts = await fetch('https://api.example.com/posts').then(r => r.json());
  
  return (
    <div>
      <h1>Blog</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  );
}

// βœ… Rendered at build time
// βœ… Fetch data once during build
// βœ… HTML + data cached
// βœ… No data fetching on request

Explicitly Static

app/docs/page.tsx
export const dynamic = 'force-static'; // Force static rendering

export default async function DocsPage() {
  const docs = await fetch('https://api.example.com/docs', {
    cache: 'force-cache', // Explicitly cache
  }).then(r => r.json());
  
  return (
    <div>
      <h1>Documentation</h1>
      {/* Render docs */}
    </div>
  );
}

// βœ… Forced to be static
// βœ… Even if it uses dynamic-like patterns
// βœ… Good for ensuring pages are always static

When Pages Are Static

βœ… Pages are Static when:

  • No dynamic functions (cookies, headers, searchParams)
  • All data fetching uses default caching (cache: 'force-cache')
  • No dynamic segments without generateStaticParams
  • export const dynamic = 'force-static' is set

Dynamic Rendering (SSR)

Using Dynamic Functions

app/dashboard/page.tsx
import { cookies } from 'next/headers';

// This page is DYNAMIC because it uses cookies()
export default async function DashboardPage() {
  const cookieStore = cookies();
  const userId = cookieStore.get('userId')?.value;
  
  // Fetch user-specific data
  const userData = await fetch(`https://api.example.com/users/${userId}`).then(r => r.json());
  
  return (
    <div>
      <h1>Welcome, {userData.name}!</h1>
      <p>Your personal dashboard</p>
    </div>
  );
}

// βœ… Rendered on every request
// βœ… Fresh user data every time
// βœ… Personalized content
// ⚠️ Slower than static (server processing)

Using headers()

app/api-docs/page.tsx
import { headers } from 'next/headers';

// Dynamic because of headers()
export default async function ApiDocsPage() {
  const headersList = headers();
  const userAgent = headersList.get('user-agent');
  
  // Customize based on device
  const isMobile = /Mobile/i.test(userAgent || '');
  
  return (
    <div>
      <h1>API Documentation</h1>
      {isMobile ? <MobileDocs /> : <DesktopDocs />}
    </div>
  );
}

// βœ… Personalized based on request headers
// βœ… Different content per device
// βœ… Dynamic rendering

Using searchParams

app/search/page.tsx
// Dynamic because of searchParams
export default async function SearchPage({
  searchParams,
}: {
  searchParams: { q?: string; page?: string };
}) {
  const query = searchParams.q || '';
  const page = parseInt(searchParams.page || '1', 10);
  
  // Search based on query parameters
  const results = await fetch(
    `https://api.example.com/search?q=${query}&page=${page}`
  ).then(r => r.json());
  
  return (
    <div>
      <h1>Search Results for "{query}"</h1>
      {results.map(result => (
        <div key={result.id}>{result.title}</div>
      ))}
    </div>
  );
}

// βœ… Different content per query
// βœ… Dynamic based on URL params
// βœ… Rendered on request

Opting Out of Caching

app/live-data/page.tsx
// Dynamic because of cache: 'no-store'
export default async function LiveDataPage() {
  const data = await fetch('https://api.example.com/live', {
    cache: 'no-store', // Don't cache, always fetch fresh
  }).then(r => r.json());
  
  return (
    <div>
      <h1>Live Data</h1>
      <p>Updated: {new Date().toLocaleTimeString()}</p>
      <p>Value: {data.value}</p>
    </div>
  );
}

// βœ… Fresh data on every request
// βœ… No caching
// βœ… Dynamic rendering

Explicitly Dynamic

app/dashboard/page.tsx
export const dynamic = 'force-dynamic'; // Force dynamic rendering

export default async function DashboardPage() {
  // Even without dynamic functions, this is dynamic
  const data = await fetch('https://api.example.com/data').then(r => r.json());
  
  return <div>{data.content}</div>;
}

// βœ… Forced to be dynamic
// βœ… Rendered on every request
// βœ… Never cached

When Pages Are Dynamic

⚠️ Pages are Dynamic when:

  • Using cookies(), headers(), or searchParams
  • Using cache: 'no-store' in fetch
  • Using revalidate: 0 in fetch
  • export const dynamic = 'force-dynamic' is set
  • Dynamic segments without generateStaticParams

Incremental Static Regeneration (ISR)

Time-Based Revalidation

app/blog/[slug]/page.tsx
// ISR: Static with revalidation every hour
export default async function BlogPostPage({
  params,
}: {
  params: { slug: string };
}) {
  const post = await fetch(`https://api.example.com/posts/${params.slug}`, {
    next: { revalidate: 3600 }, // Revalidate every 1 hour (3600 seconds)
  }).then(r => r.json());
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      <p>Published: {post.publishedAt}</p>
    </article>
  );
}

// βœ… Generated statically at build
// βœ… Served from cache for 1 hour
// βœ… After 1 hour, regenerates in background
// βœ… Subsequent requests get updated version
// βœ… Fast + fresh data

Route Segment Config

app/products/page.tsx
// Revalidate entire route segment
export const revalidate = 3600; // 1 hour

export default async function ProductsPage() {
  const products = await fetch('https://api.example.com/products').then(r => r.json());
  
  return (
    <div>
      <h1>Products</h1>
      {products.map(product => (
        <div key={product.id}>
          <h2>{product.name}</h2>
          <p>${product.price}</p>
        </div>
      ))}
    </div>
  );
}

// βœ… Applies to entire route
// βœ… All fetches in this route use same revalidation
// βœ… Simpler than per-fetch revalidation

Different Revalidation Times

app/dashboard/page.tsx
export default async function DashboardPage() {
  // Revalidate every 60 seconds
  const stats = await fetch('https://api.example.com/stats', {
    next: { revalidate: 60 },
  }).then(r => r.json());
  
  // Revalidate every 10 minutes
  const notifications = await fetch('https://api.example.com/notifications', {
    next: { revalidate: 600 },
  }).then(r => r.json());
  
  // Never cache (always fresh)
  const liveData = await fetch('https://api.example.com/live', {
    cache: 'no-store',
  }).then(r => r.json());
  
  return (
    <div>
      <h1>Dashboard</h1>
      <Stats data={stats} />
      <Notifications data={notifications} />
      <LiveFeed data={liveData} />
    </div>
  );
}

// βœ… Different revalidation per data source
// βœ… Optimize based on update frequency
// βœ… Mix static, ISR, and dynamic data

ISR with generateStaticParams

app/products/[id]/page.tsx
// Generate static paths at build time
export async function generateStaticParams() {
  const products = await fetch('https://api.example.com/products').then(r => r.json());
  
  return products.map((product) => ({
    id: product.id,
  }));
}

// ISR for each product
export default async function ProductPage({
  params,
}: {
  params: { id: string };
}) {
  const product = await fetch(`https://api.example.com/products/${params.id}`, {
    next: { revalidate: 3600 }, // Revalidate every hour
  }).then(r => r.json());
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <p>${product.price}</p>
    </div>
  );
}

// βœ… Pre-generate known products at build
// βœ… Each product revalidates independently
// βœ… New products generated on-demand
// βœ… Fast + always up-to-date

Rendering Strategies by Route

Different rendering strategies for different routes

appImportant

Select a file or folder to see details

Rendering Comparison

FeatureStatic (SSG)Dynamic (SSR)ISR
When RenderedBuild timeRequest timeBuild + revalidation
Performance⚑ Fastest🐒 Slower⚑ Fast
Data FreshnessStale until rebuildAlways freshPeriodic updates
CachingIndefinite CDNNo cachingCDN with TTL
Server LoadNone (cached)High (every request)Low (periodic)
Best ForMarketing, docs, blogDashboards, personalizedE-commerce, news, CMS
Build TimeIncreases with pagesFast buildsBuild known paths

Cache Control Options

Force Cache (Static)

TYPESCRIPT
// Force caching - always static
const data = await fetch('https://api.example.com/data', {
  cache: 'force-cache', // Default behavior
});

// βœ… Cached at build time
// βœ… Never refetches
// βœ… Fastest option

No Store (Dynamic)

TYPESCRIPT
// Never cache - always dynamic
const data = await fetch('https://api.example.com/data', {
  cache: 'no-store', // Opt out of caching
});

// βœ… Fresh data every request
// βœ… Dynamic rendering
// βœ… Use for real-time data

Revalidate (ISR)

TYPESCRIPT
// Cache with revalidation - ISR
const data = await fetch('https://api.example.com/data', {
  next: { revalidate: 60 }, // Revalidate every 60 seconds
});

// βœ… Static + periodic updates
// βœ… Best of both worlds
// βœ… Use for frequently updated content

No Revalidate (Static Forever)

TYPESCRIPT
// Cache forever
const data = await fetch('https://api.example.com/data', {
  next: { revalidate: false }, // Or omit revalidate
});

// βœ… Cached at build
// βœ… Never revalidates
// βœ… Use for truly static content

Revalidate 0 (Dynamic)

TYPESCRIPT
// Revalidate immediately - effectively dynamic
const data = await fetch('https://api.example.com/data', {
  next: { revalidate: 0 },
});

// βœ… Same as cache: 'no-store'
// βœ… Always fresh
// βœ… Dynamic rendering

Route Segment Config Options

dynamic

app/page.tsx
// Control rendering mode
export const dynamic = 'auto'; // Default: auto-detect
// export const dynamic = 'force-static'; // Always static
// export const dynamic = 'force-dynamic'; // Always dynamic
// export const dynamic = 'error'; // Error if dynamic

export default function Page() {
  return <div>Content</div>;
}

// 'auto': Next.js decides based on usage
// 'force-static': Force static even with dynamic functions
// 'force-dynamic': Force dynamic even without dynamic functions
// 'error': Throw error if page becomes dynamic

revalidate

app/blog/page.tsx
// Set revalidation for entire route
export const revalidate = 3600; // 1 hour
// export const revalidate = 60; // 1 minute
// export const revalidate = false; // Never revalidate
// export const revalidate = 0; // Revalidate on every request (dynamic)

export default async function BlogPage() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json());
  return <div>{/* posts */}</div>;
}

// Applies to all data fetching in this route
// Can be overridden per-fetch with next.revalidate

dynamicParams

app/products/[id]/page.tsx
// Control behavior for dynamic params not in generateStaticParams
export const dynamicParams = true; // Default: generate on-demand
// export const dynamicParams = false; // Return 404 if not pre-generated

export async function generateStaticParams() {
  // Generate params for known products
  return [
    { id: '1' },
    { id: '2' },
    { id: '3' },
  ];
}

export default function ProductPage({ params }: { params: { id: string } }) {
  return <div>Product {params.id}</div>;
}

// dynamicParams = true: New products generated on first visit
// dynamicParams = false: Only pre-generated products exist

Rendering Best Practices

1. Default to Static

TYPESCRIPT
// βœ… GOOD: Let Next.js optimize
export default async function Page() {
  const data = await fetch('https://api.example.com/data');
  return <div>{/* render */}</div>;
}

// ❌ BAD: Forcing dynamic unnecessarily
export const dynamic = 'force-dynamic';
export default function Page() {
  // Could be static but forced dynamic
  return <div>Static content</div>;
}

// Use static rendering by default for best performance

2. Use ISR for Semi-Dynamic Content

TYPESCRIPT
// βœ… GOOD: ISR for content that changes occasionally
export const revalidate = 3600; // 1 hour

export default async function ProductsPage() {
  const products = await fetch('https://api.example.com/products').then(r => r.json());
  return <div>{/* products */}</div>;
}

// ❌ BAD: Dynamic for content that rarely changes
export const dynamic = 'force-dynamic';
// Causes unnecessary server load

// ISR provides fast loads + fresh content

3. Reserve Dynamic for Personalized Content

TYPESCRIPT
// βœ… GOOD: Dynamic for user-specific data
import { cookies } from 'next/headers';

export default async function DashboardPage() {
  const userId = cookies().get('userId')?.value;
  const userData = await fetch(`/api/users/${userId}`).then(r => r.json());
  return <div>Welcome, {userData.name}</div>;
}

// Dynamic rendering justified for personalized content

4. Choose Appropriate Revalidation Times

TYPESCRIPT
// βœ… GOOD: Match revalidation to update frequency

// Blog posts: 1 hour (rarely change)
export const revalidate = 3600;

// Product prices: 5 minutes (change frequently)
export const revalidate = 300;

// News articles: 1 minute (very frequent)
export const revalidate = 60;

// Static content: never
export const revalidate = false;

// Match revalidation to your content update frequency

5. Use generateStaticParams for Known Routes

TYPESCRIPT
// βœ… GOOD: Pre-generate known routes
export async function generateStaticParams() {
  const products = await fetch('https://api.example.com/products').then(r => r.json());
  return products.map(p => ({ id: p.id }));
}

// Faster builds, better performance for known routes

// ❌ BAD: All routes generated on-demand
// Slower first visits, increased server load

// Pre-generate what you know, on-demand for the rest

Key Takeaways

  • Static (SSG) - build time, cached forever, fastest
  • Dynamic (SSR) - request time, always fresh, personalized
  • ISR - static + periodic updates, best of both
  • Default to static - Next.js optimizes automatically
  • cache: 'no-store' - opts into dynamic rendering
  • revalidate - time-based ISR (seconds)
  • Dynamic functions - cookies(), headers(), searchParams trigger dynamic
  • Choose wisely - match rendering to content type

What's Next?

You've mastered static and dynamic rendering! Next, we'll explore generateStaticParams for Static Generationβ€”pre-generating dynamic routes at build time, controlling which paths to generate, implementing fallback behavior, and optimizing build times. You'll create lightning-fast static sites with dynamic routes!

We'll cover generateStaticParams, dynamicParams, fallback behavior, and complete static generation strategies.

⚑ Performance Tip

Choose the right rendering strategy: Static for marketing pages and blogs (fastest), ISR for e-commerce and news (fast + fresh), Dynamic for dashboards and personalized content (always current). Match your rendering to your content update frequency!

Test Your Understanding

Question 1 of 4

What is Static Rendering (SSG)?

Master static and dynamic rendering in Next.js! Learn SSG, SSR, ISR, and when to use each.

Previous
Not Found and Global Error Pages
Next
generateStaticParams for Static Generation

Master Next.js Performance

Join 2,000+ developers building blazing-fast Next.js apps. Get the next lesson on generateStaticParams - 100% FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

NextJS Tutorials

0 of 70 completed

Your Progress0%

Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

Β© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo