Server-Side Rendering (SSR)
Server-Side Rendering means the HTML for a page is generated on the server fresh, for every single request. The user always sees the most current data, at the cost of doing that rendering work again and again rather than reusing a cached result.
When SSR Is the Right Fit
Content that changes frequently — stock prices, live scores, inventory counts.
Personalized content that differs per user — a dashboard, an account page, a shopping cart.
Pages that must reflect the exact state of the world at the moment of the request.
No Special Function Needed
In the Pages Router, SSR required exporting a specific function, getServerSideProps, from the page file. The App Router removes that requirement entirely — a route becomes server-rendered automatically the moment it uses a dynamic data source, with no additional configuration.
// app/account/page.tsx
import { cookies } from 'next/headers'
export default async function AccountPage() {
const sessionId = cookies().get('session_id')?.value
const account = await fetchAccount(sessionId)
return <h1>Welcome back, {account.name}</h1>
}
// Reading cookies() makes this route render dynamically, per request —
// no getServerSideProps, no special export needed.What Triggers Dynamic (SSR) Rendering
Next.js decides a route is dynamic — and therefore server-rendered per request — as soon as it detects any of the following inside that route's Server Components.
Dynamic Source | Example |
|---|---|
Reading cookies | cookies().get('session_id') |
Reading headers | headers().get('user-agent') |
A fetch call with caching disabled | fetch(url, { cache: 'no-store' }) |
Reading the searchParams prop in a page | export default function Page({ searchParams }) { ... } |
Calling useSearchParams from a Client Component in the tree | const params = useSearchParams() |
A Second Example: Personalization via Headers
// app/page.tsx
import { headers } from 'next/headers'
export default async function HomePage() {
const userAgent = headers().get('user-agent') ?? ''
const isMobile = /mobile/i.test(userAgent)
return <h1>{isMobile ? 'Mobile view' : 'Desktop view'}</h1>
}The Trade-off
SSR guarantees freshness, but that guarantee has a real cost: every request re-runs your rendering logic and any data fetching it depends on. A page that's expensive to render, or that fetches from a slow upstream API, will feel that cost on every single visit — there's no cached HTML to serve instantly the way there is with SSG.
Pros: always fresh, correctly personalized, no stale-cache concerns.
Cons: slower time-to-first-byte than a cached static page, more server load at scale, no CDN-level HTML caching without extra work.
SSR is the right tool whenever a page's output genuinely depends on the specific request — who's asking, what cookies they sent, what's true right now. For everything else, Static Site Generation, covered next, is usually the better default.