Next.js supports multiple rendering strategies per page. You choose a strategy by exporting specific functions from a page file.
Static Generation (SSG)
With Static Generation, the page HTML is generated at build time. The HTML is reused on every request and can be cached by a CDN.
Without data
Pages that do not export getStaticProps or getServerSideProps are automatically statically generated:
export default function About() {
return <div>About</div>
}
With data
Export getStaticProps to fetch data at build time:
import type { GetStaticProps, InferGetStaticPropsType } from 'next'
type Post = { id: string; title: string }
export const getStaticProps = (async () => {
const res = await fetch('https://api.example.com/posts')
const posts: Post[] = await res.json()
return { props: { posts } }
}) satisfies GetStaticProps<{ posts: Post[] }>
export default function Blog({
posts,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
When to use Static Generation
Use Static Generation for content that can be built once and served to all users:
- Marketing pages
- Blog posts
- Product listings
- Documentation
Ask yourself: can this page be prerendered before a user requests it? If yes, use Static Generation.
Server-side Rendering (SSR)
With Server-side Rendering, the page HTML is generated on every request.
Export getServerSideProps to opt a page into SSR:
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
type Repo = {
name: string
stargazers_count: number
}
export const getServerSideProps = (async () => {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const repo: Repo = await res.json()
return { props: { repo } }
}) satisfies GetServerSideProps<{ repo: Repo }>
export default function Page({
repo,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return <p>Stars: {repo.stargazers_count}</p>
}
When to use SSR
Use SSR when a page:
- Displays personalized data (user profile, session-based content).
- Depends on request-time information (cookies, headers, geolocation).
- Shows data that changes too frequently to benefit from caching.
SSR pages cannot be cached by a CDN at the page level. If you need caching, add Cache-Control headers in getServerSideProps, or consider using ISR instead.
Incremental Static Regeneration (ISR)
ISR lets you update static pages after build time without rebuilding the entire site. Add a revalidate field to the return value of getStaticProps:
import type { GetStaticProps, GetStaticPaths, InferGetStaticPropsType } from 'next'
type Product = { id: string; name: string; price: number }
export const getStaticPaths = (async () => {
const res = await fetch('https://api.example.com/products')
const products: Product[] = await res.json()
return {
paths: products.map((p) => ({ params: { id: p.id } })),
fallback: 'blocking',
}
}) satisfies GetStaticPaths
export const getStaticProps = (async ({ params }) => {
const res = await fetch(`https://api.example.com/products/${params?.id}`)
const product: Product = await res.json()
return {
props: { product },
revalidate: 60, // regenerate at most once per minute
}
}) satisfies GetStaticProps<{ product: Product }>
export default function ProductPage({
product,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return <h1>{product.name}</h1>
}
With revalidate: 60:
- The cached page is served immediately.
- If more than 60 seconds have passed since the last generation, Next.js regenerates the page in the background.
- The next request receives the freshly generated page.
On-demand revalidation
You can trigger revalidation from an API route:
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const path = req.query.path as string
await res.revalidate(path)
return res.json({ revalidated: true })
}
Automatic Static Optimization
Next.js automatically determines whether a page can be statically generated. If a page does not export getServerSideProps or getInitialProps, it is treated as static.
The absence of blocking data requirements is what triggers this optimization.
Adding getInitialProps to your custom _app.js disables Automatic Static Optimization for all pages that do not use getStaticProps.
Client-side rendering (CSR)
For pages or components where you do not need server-rendered HTML, fetch data on the client.
Using useEffect
import { useState, useEffect } from 'react'
export default function Page() {
const [data, setData] = useState(null)
useEffect(() => {
fetch('/api/data')
.then((res) => res.json())
.then(setData)
}, [])
return <p>{data ? JSON.stringify(data) : 'Loading...'}</p>
}
Using SWR
SWR is the recommended library for client-side data fetching. It handles caching, revalidation, and more:
import useSWR from 'swr'
const fetcher = (url: string) => fetch(url).then((r) => r.json())
export default function Page() {
const { data, error, isLoading } = useSWR('/api/data', fetcher)
if (error) return <p>Failed to load.</p>
if (isLoading) return <p>Loading...</p>
return <p>Your data: {JSON.stringify(data)}</p>
}
CSR can affect SEO because the page is empty until JavaScript runs. For content that must be indexed, use Static Generation or SSR instead.