Rendering strategies: SSR, SPA and prerendering
So far every blog page has been rendered on the server on every request — that is SSR (server-side rendering). But not every site needs a running Node.js server. React Router supports two more strategies: SPA mode, where there is no server at all, and prerendering, where page HTML is generated once at build time. The strategy is chosen in react-router.config.ts.
The config file
It already exists in the project root:
// react-router.config.ts
import type { Config } from "@react-router/dev/config";
export default {
ssr: true,
} satisfies Config;ssr: true is the default: for every request the server calls the loader and renders HTML.
Prerendering
Pages that rarely change can be generated in advance. The prerender option takes a list of addresses:
// react-router.config.ts
import type { Config } from "@react-router/dev/config";
export default {
ssr: true,
prerender: ["/", "/about"],
} satisfies Config;On npm run build the framework calls these pages' loaders and saves ready HTML files to build/client. Other pages are still rendered by the server. prerender: true generates all addresses without dynamic segments.
A list from data
For addresses with params prerender can be a function:
// react-router.config.ts
import type { Config } from "@react-router/dev/config";
export default {
async prerender() {
const ids = [1, 2, 3]; // for example, from the database
return ["/", "/about", ...ids.map((id) => `/posts/${id}`)];
},
} satisfies Config;After the build, /posts/1, /posts/2 and /posts/3 exist as ready files.
SPA mode
// react-router.config.ts
import type { Config } from "@react-router/dev/config";
export default {
ssr: false,
} satisfies Config;With ssr: false the build produces a static site: build/client/index.html and JavaScript. You can put it on any static host. There are no server loader and action functions in this mode — data is loaded by clientLoader and clientAction in the browser. Prerendering can be combined with this mode too.
Comparison
| Strategy | Node.js server | Data freshness | Good for |
|---|---|---|---|
| SSR | Required | Every request | A blog with comments, user accounts |
| Prerendering | Not needed for those pages | At build time | "About us", documentation |
| SPA | Not needed | From the browser | Internal dashboards behind a login |
Common mistake
Turning on ssr: false in a project that already has server loader functions with a database and sessions, and expecting everything to keep working. Without a server there is nowhere to run that code, and the build reports an error. Choose the rendering strategy early: a blog with comments and login needs SSR, and prerendering is added for individual static pages.