Rendering is the process of creating a user interface (UI) from code and Next.js has a couple of different ways of rendering the UI (which ways we call strategies). Understanding how each of these rendering strategies impacts SEO performance, Core Web Vitals, and user experience is absolutely critical to the success of your web app.
That's what we shall be discussing in this article.
Why Rendering Strategy Matters
When building a web app with Next.js, how your content gets rendered determines:
- How search engines index your site.
- How fast users see meaningful content.
- How well your site scores on Core Web Vitals.
Choosing the right rendering method can improve your SEO rankings, reduce bounce rates, and enhance user satisfaction.
Rendering Strategies in Next.js
Next.js supports four main rendering strategies:
- Static Site Generation (SSG)
- Server-Side Rendering (SSR)
- Incremental Static Regeneration (ISR)
- Client-Side Rendering (CSR)
Let’s break them down one by one.
1. Static Site Generation (SSG)
With SSG, pages are rendered at build time (when you run next build
). The HTML is generated once and served to every user.
How to use it:
import { getPostBySlug, getAllPostSlugs } from "@/lib/posts";export const dynamicParams = false;export async function generateStaticParams() {const slugs = await getAllPostSlugs();return slugs.map((slug) => ({ slug }));}export default async function BlogPostPage({ params }: { params: { slug: string } }) {const post = await getPostBySlug(params.slug);return (<article className="prose dark:prose-invert mx-auto py-10"><h1>{post.title}</h1><p className="text-sm opacity-70">{new Date(post.publishedAt).toDateString()}</p><div dangerouslySetInnerHTML={{ __html: post.html }} /></article>);}
Let's briefly explain what some of the code above is doing:
export const dynamicParams = false;
Setting dynamicParams to false This tells Next.js: Only generate pages for the slugs you explicitly return in generateStaticParams()
.
That means If a user visits /blog/how-to-build-saas
and that slug wasn’t prebuilt, they get a 404 page instead of trying to build it on-demand. If dynamicParams were set to true, that page would be built on demand.
export async function generateStaticParams() {const slugs = await getAllPostSlugs();return slugs.map((slug) => ({ slug }));}
generateStaticParams
runs at build time and tells Next.js which dynamic routes to pre-render.
const post = await getPostBySlug(params.slug);
This fetch runs once at build-time and the data returned is baked into the final page HTML content.
Pros:
- Blazing-fast performance (especially if served via CDN).
- Great for SEO since full HTML is ready at load.
- Predictable—no runtime server errors.
- Reduces on the server load.
Cons:
- Content is stale until next build.
- Not ideal for frequently changing content.
Best for:
- Blogs
- Docs
- Marketing pages
SSG is the best strategy for SEO since the HTML is pre-generated and ready for search engine crawlers to index.
It also minimizes the request-response time between the client and server which is a strong SEO ranking factor (page speed).
2. Server-Side Rendering (SSR)
SSR pages are dynamically rendered on each request by the server.
How to use it:
import { notFound } from "next/navigation";import { getPostBySlug } from "@/lib/posts";export default async function BlogPostPage({ params }: { params: { slug: string } }) {const post = await getPostBySlug(params.slug);if (!post) return notFound();return (<article className="prose dark:prose-invert mx-auto py-10"><h1>{post.title}</h1><p className="text-sm opacity-70">{new Date(post.publishedAt).toDateString()}</p><div dangerouslySetInnerHTML={{ __html: post.html }} /></article>);}
const post = await getPostBySlug(params.slug);
getPostBySlug
runs each time on the server per request.
Pros:
- Fresh data every time.
- Good for personalized content or data-sensitive pages.
Cons:
- Slower than SSG—must wait for server response.
- Heavier server load and complexity.
Best for:
- Authenticated dashboards
- Real-time data (e.g. stock prices)
But we can have the best of SSR and SSG by using ISR which we shall discuss next.
3. Incremental Static Regeneration (ISR)
ISR is like SSG, but allows you to update static pages after build.
How to use it:
interface Post {slug: string;title: string;html: string;publishedAt: string;}export const revalidate = 3600;export async function generateStaticParams() {const res = await fetch("https://tesla.com/blog",{ cache: "force-cache" });const posts: { slug: string }[] = await res.json();return posts.map((p) => ({ slug: p.slug }));}export default async function BlogPostPage({ params }: { params: { slug: string } }) {const res = await fetch(`https://tesla.com/blog",/${params.slug}`);if (!res.ok) {throw new Error("Failed to load post");}const post: Post = await res.json();return (<article className="prose dark:prose-invert mx-auto py-10"><h1>{post.title}</h1><p className="text-sm opacity-70">{new Date(post.publishedAt).toDateString()}</p><divdangerouslySetInnerHTML={{ __html: post.html }}/></article>);}
This page is revalidated after every 1 hour (3600) and thus the content is kept fresh.
Pros:
- Combines speed of SSG with freshness of SSR.
- No rebuilds needed for every change.
Cons:
- Delay in update visibility.
- Slightly more complex to debug.
Best for:
- Blogs with frequent updates
- Product pages that change often
Client-Side Rendering (CSR)
With CSR the page shell is served, and content is fetched/rendered in the browser using JavaScript. Client components have a use client
directive at the top of the file.
How to use it:
"use client";import { useEffect, useState } from "react";type Post = { title: string; html: string; publishedAt: string };export default function ClientPost({ slug }: { slug: string }) {const [post, setPost] = useState<Post | null>(null);useEffect(() => {fetch(`https://tesla.com/blog/${slug}`).then((res) => res.json()).then(setPost).catch(() => setPost(null));}, [slug]);if (!post) return <p>Loading...</p>;return (<article className="prose dark:prose-invert mx-auto py-10"><h1>{post.title}</h1><p className="text-sm opacity-70">{new Date(post.publishedAt).toDateString()}</p><div dangerouslySetInnerHTML={{ __html: post.html }} /></article>);}
Pros:
- Fully dynamic.
- Great for user-specific or private content.
Cons:
- Search engines may miss content (bad for SEO).
- Blank page until JS loads.
Best for:
- Admin dashboards
- Auth-gated areas
SEO Summary Table
Rendering Method | SEO Performance | Page Speed | Content Freshness | Use Case |
SSG | Excellent | Fast | Static | Blogs, Docs |
SSR | Great | Slower | Always Fresh | Dashboards |
ISR | Excellent | Fast | Pretty Fresh | Product Pages |
CSR | Poor | Medium | Fresh | User Portals |
Recommendations
Page Type | Recommended Strategy |
Blog Home | ISR or SSG |
Blog Post | ISR or SSG |
Product Page | ISR |
Authenticated Dashboard | SSR or CSR |
Pricing Page | SSG |
Landing Page | SSG |
Conclusion
Your choice of rendering strategy should be based on:
- How often your data changes
- Whether SEO is important for the page
- The trade-off between speed and freshness
By leveraging ISR and SSG wisely, you can get the best of both worlds — speed and SEO.