SSR, CSR, and Prerendering
The three ways a SvelteKit page can render, and how to choose per-route with page options.
3 phút đọc
SvelteKit doesn't force a single rendering strategy on your whole app — it can render different routes differently, chosen with a few exported options per route. Understanding what each one actually does is the key to picking correctly instead of guessing.
Server-side rendering (SSR): the default
By default, every page is rendered to HTML on the server for the first request, then "hydrated" in the browser — the same Svelte components run again client-side, attaching event listeners to the existing DOM rather than rebuilding it. The user sees real content immediately, before any JavaScript has even downloaded, and it's what makes SvelteKit pages SEO-friendly and fast on slow connections.
Subsequent navigations (clicking an <a> to another SvelteKit route) skip full SSR — SvelteKit fetches just the data via load and renders the new page client-side, which is why in-app navigation feels instant after the first load.
Turning SSR off
// src/routes/dashboard/+page.js
export const ssr = false;With ssr disabled, this route renders an empty shell on the server and does everything in the browser — effectively becoming a client-side-only page for just this route. This trades away SEO and first-paint speed, so it's usually reserved for pages that are heavily interactive, behind a login wall, and never need to be indexed — an admin dashboard, for instance.
Turning client-side JavaScript off
// src/routes/legal/+page.js
export const csr = false;The inverse: the page still server-renders, but no JavaScript is shipped for it at all, so it never hydrates and stays fully static/non-interactive after load. This suits genuinely static content — a terms-of-service page — where shipping any JS at all is pure waste.
Prerendering: SSR done once, at build time
// src/routes/about/+page.js
export const prerender = true;Prerendering runs a page's load and render logic once, during the build, and saves the resulting HTML as a static file — served instantly with no server computation per request, just like a plain HTML file. This is the right choice for pages whose content doesn't depend on the specific visitor or request: marketing pages, documentation, a blog post once published.
It's wrong for anything personalized (/dashboard, showing locals.user) since prerendering happens once at build time, with no per-request locals available — the same HTML would be served to everyone.
Prerendering an entire app
// src/routes/+layout.js
export const prerender = true;Setting prerender in the root layout makes it the default for every route, which — combined with adapter-static (the next lesson) — turns a whole SvelteKit app into a folder of static files, deployable anywhere that serves static assets, with no Node.js server required at runtime at all.
Choosing per route
A realistic app mixes all three:
// src/routes/+page.js → prerender: true (marketing homepage)
// src/routes/blog/[slug]/+page.js → prerender: true (published posts)
// src/routes/dashboard/+page.js → ssr: false (personalized, client-only)
// src/routes/legal/+page.js → csr: false (static, no interactivity)The mental model that ties all three together: SSR is about where the first render happens (server vs. browser), CSR is about whether JavaScript ever takes over afterward, and prerendering is about when SSR happens (per-request vs. once, at build time). Most routes are fine with the defaults — reach for these options only when a specific route's needs genuinely differ.