Prerendering
Prerendering outputs static HTML for routes at build time, combining SPA interactivity with indexable HTML for SEO.
Definition
Prerendering renders routes during build and writes HTML files per URL. It's common for React Router sites: serve HTML first for SEO, then hydrate on the client for interactivity — without full SSR at runtime.
Why it matters
- Indexable HTML per route for more reliable crawling
- Fast delivery via CDN-served static files with minimal TTFB
- Cheaper than full SSR for content-heavy sites — no server needed
- SEO-friendly: ensures search engines see complete HTML with meta tags
- Compatible with any static host (Cloudflare Pages, Netlify, Vercel)
- Preserves SPA interactivity — no sacrifice on user experience
- Easier debugging: view each page's output at build time
How to implement
- List routes to prerender (home, tutorials, tools, glossary)
- Make head tags route-aware (title/canonical/hreflang/schema)
- Avoid SPA catch-all rewrites that override per-route index.html
- Use react-snap, vite-plugin-ssr, or write a custom prerender script
- Output each route to its own index.html (e.g., /glossary/seo/index.html)
- Embed JSON-LD and Open Graph during the prerender phase
- Validate after build: curl each route to confirm complete HTML
Examples
html
// scripts/prerender.ts
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom/server';
import App from '../src/App';
import fs from 'fs';
const routes = ['/', '/glossary/seo', '/tools/sitemap'];
for (const route of routes) {
const html = renderToString(
<StaticRouter location={route}>
<App />
</StaticRouter>
);
const dir = `dist${route}`;
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(`${dir}/index.html`, `<!DOCTYPE html>${html}`);
}html
// package.json
{
"scripts": {
"build": "vite build",
"postbuild": "react-snap"
},
"reactSnap": {
"source": "dist",
"inlineCss": true,
"puppeteerArgs": ["--no-sandbox"]
}
}Related
Tutorials
FAQ
Common questions about this term.