rendering methods

9/8/20255 min read

rendering methods

next.js gives you four ways to render pages. each has its use case.

CSR (client-side rendering)

javascript renders in the browser after page loads.

flow:

  1. server sends minimal HTML + JS bundles
  2. browser downloads & executes JS
  3. JS fetches data from APIs
  4. react renders
  5. page becomes interactive

use for:

  • dashboards with frequent interactions
  • admin panels requiring auth
  • real-time apps with live data
  • interactive tools/calculators
  • private content (no SEO needed)
import { useState, useEffect } from 'react';

export default function Dashboard() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchData() {
      const res = await fetch('/api/user/profile');
      const json = await res.json();
      setData(json);
      setLoading(false);
    }
    fetchData();
  }, []);

  if (loading) return <div>loading...</div>;

  return <div>welcome, {data.name}!</div>;
}

pros:

  • less server load
  • great for dynamic content
  • smooth client transitions

cons:

  • slow initial load
  • poor SEO
  • blank page while loading

SSR (server-side rendering)

page rendered on server for each request.

flow:

  1. request hits server
  2. server fetches data
  3. server renders HTML
  4. browser receives full HTML
  5. JS hydrates for interactivity

use for:

  • personalized content (user dashboards)
  • frequently updated data
  • SEO-critical pages with dynamic data
  • pages needing fresh data on every visit
export default function Profile({ user }) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>last login: {user.lastLogin}</p>
    </div>
  );
}

export async function getServerSideProps(context) {
  const { userId } = context.params;
  const res = await fetch(`https://api.example.com/users/${userId}`);
  const user = await res.json();

  return { props: { user } };
}

pros:

  • good SEO
  • fast first contentful paint
  • always fresh data

cons:

  • higher server load
  • slower than static
  • can't cache easily

SSG (static site generation)

pages generated at build time.

flow:

  1. build process fetches data
  2. generates static HTML files
  3. files served from CDN
  4. instant page loads

use for:

  • blog posts
  • marketing pages
  • documentation
  • product pages
  • content that doesn't change often
export default function BlogPost({ post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  );
}

export async function getStaticProps({ params }) {
  const post = await getPost(params.slug);
  return { props: { post } };
}

export async function getStaticPaths() {
  const posts = await getAllPosts();
  const paths = posts.map(post => ({
    params: { slug: post.slug }
  }));

  return { paths, fallback: false };
}

pros:

  • blazing fast
  • great SEO
  • low server cost
  • can use CDN

cons:

  • data can be stale
  • long build times with many pages
  • need rebuild for updates

ISR (incremental static regeneration)

static pages that update periodically.

flow:

  1. serve static page
  2. check if revalidation time passed
  3. regenerate in background if needed
  4. serve fresh page on next request

use for:

  • e-commerce product pages
  • news sites
  • content that updates occasionally
  • high-traffic pages needing some freshness
export default function Product({ product }) {
  return (
    <div>
      <h1>{product.name}</h1>
      <p>price: ${product.price}</p>
      <p>stock: {product.stock}</p>
    </div>
  );
}

export async function getStaticProps({ params }) {
  const product = await getProduct(params.id);

  return {
    props: { product },
    revalidate: 60 // regenerate every 60 seconds
  };
}

export async function getStaticPaths() {
  return {
    paths: [],
    fallback: 'blocking' // or true
  };
}

pros:

  • fast like SSG
  • data stays relatively fresh
  • scales well
  • good SEO

cons:

  • can serve stale data briefly
  • complex caching behavior
  • fallback handling needed

fallback options

fallback: false

  • only pre-rendered paths work
  • 404 for others

fallback: true

  • shows fallback UI while generating
  • page generated on first request
export default function Page({ data }) {
  const router = useRouter();

  if (router.isFallback) {
    return <div>loading...</div>;
  }

  return <div>{data.title}</div>;
}

fallback: 'blocking'

  • waits for page generation
  • no fallback UI needed
  • better for SEO

on-demand revalidation

trigger revalidation manually:

// pages/api/revalidate.js
export default async function handler(req, res) {
  const { secret, path } = req.query;

  // check secret
  if (secret !== process.env.REVALIDATE_SECRET) {
    return res.status(401).json({ message: 'invalid token' });
  }

  try {
    await res.revalidate(path);
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send('error revalidating');
  }
}

trigger it:

curl https://yoursite.com/api/revalidate?secret=YOUR_SECRET&path=/blog/post-1

mixing strategies

you can use different methods for different pages:

/                    → SSG (homepage)
/blog                → SSG (blog list)
/blog/[slug]         → ISR (blog posts)
/dashboard           → CSR (user dashboard)
/profile/[id]        → SSR (user profiles)
/products/[id]       → ISR (product pages)

quick comparison

| method | when rendered | data freshness | SEO | speed | server load | |--------|--------------|----------------|-----|-------|-------------| | CSR | browser | always fresh | poor | slow initial | low | | SSR | each request | always fresh | great | medium | high | | SSG | build time | stale | great | fastest | lowest | | ISR | build + periodic | mostly fresh | great | fast | low |

choosing the right method

use CSR if:

  • no SEO needed
  • highly interactive
  • user-specific data
  • real-time updates

use SSR if:

  • need SEO
  • data changes constantly
  • personalized content
  • can handle server load

use SSG if:

  • content rarely changes
  • SEO critical
  • want max performance
  • ok with rebuilds

use ISR if:

  • content updates occasionally
  • need speed + freshness balance
  • high traffic
  • SEO matters

performance tips

  • preload critical data
  • optimize images with next/image
  • lazy load components
  • use SWR for client-side caching
  • implement proper loading states
  • minimize JS bundles
  • use CDN for static assets
  • monitor core web vitals