NelsonLabs

Data Fetching

One of Next.js's biggest strengths is giving you multiple strategies for getting data into your pages. The strategy you pick determines how fast your page loads, how fresh the data is, and where the work happens.

ANALOGY

Real-world analogy: A newspaper. A printed newspaper (SSG) is fast to read but shows yesterday's news. A live news website (SSR) always shows the latest but takes a moment to load. A hybrid — where the paper prints overnight but gets updated stories slotted in throughout the day (ISR) — is often the best of both worlds.

🖥️
SSR — Server-Side Rendering
Page is built on the server on every single request. Always shows latest data.
📦
SSG — Static Generation
Page is built once at deploy time. Blazing fast. Great for content that rarely changes.
🔄
ISR — Incremental Static
Static page that automatically rebuilds every N seconds. Best of both worlds.
🌐
CSR — Client-Side Rendering
Page loads first, then fetches data in the browser. Good for user-specific content.

1. SSR — always fresh data

app/leaderboard/page.jsx — rebuilds on every request
jsx
export default async function LeaderboardPage() {
  // cache: 'no-store' tells Next.js: never cache this fetch.
  // Run it fresh every time someone loads the page — that's SSR.
  const res = await fetch("https://api.example.com/scores", {
    cache: "no-store"
  });
  const scores = await res.json();

  return (
    <ol>
      {scores.map((player, i) => (
        <li key={player.id}>{i + 1}. {player.name} — {player.score}</li>
      ))}
    </ol>
  );
}

// Use SSR for: live scores, real-time prices, user dashboards, anything personalised

2. SSG — built once, served forever

app/blog/[slug]/page.jsx — built once at deploy time
jsx
// No cache option = statically generated by default in Next.js
export default async function BlogPost({ params }) {
  const { slug } = await params;
  const res = await fetch(`https://api.example.com/posts/${slug}`);
  const post = await res.json();

  return <article><h1>{post.title}</h1><p>{post.body}</p></article>;
}

// Tell Next.js which slugs to pre-generate as static pages
export async function generateStaticParams() {
  const posts = await fetch("https://api.example.com/posts").then(r => r.json());

  return posts.map(post => ({ slug: post.slug }));
  // This generates: /blog/intro-to-coding, /blog/what-is-react, etc.
}

// Use SSG for: blog posts, documentation, marketing pages, anything that rarely changes

3. ISR — static speed with automatic updates

Rebuild this page automatically every 60 seconds
jsx
export default async function ProductPage({ params }) {
  const { id } = await params;
  const product = await fetch(`https://api.example.com/products/${id}`, {
    next: { revalidate: 60 }  // Regenerate this page every 60 seconds
  });
  const data = await product.json();

  return <div><h1>{data.name}</h1><p>Price: ${data.price}</p></div>;
}

// Use ISR for: product pages, news articles, anything updated occasionally

4. CSR — fetch in the browser

components/UserProfile.jsx — fetches after page loads
jsx
"use client";
import { useState, useEffect } from "react";

export default function UserProfile({ userId }) {
  const [profile, setProfile] = useState(null);
  const [loading, setLoading] = useState(true);

  // useEffect runs after the component first appears in the browser
  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(r => r.json())
      .then(data => {
        setProfile(data);
        setLoading(false);
      });
  }, [userId]); // Re-run if userId changes

  if (loading) return <p>Loading profile...</p>;
  return <div><h2>{profile.name}</h2><p>{profile.bio}</p></div>;
}

// Use CSR for: personalised user data, live feeds, anything unique per user