React SPA SEO from prerendering to sitemap
Put content and metadata in initial HTML and generate prerendered routes, canonicals, robots, 404 behavior, and sitemap from one manifest.
A React SPA can render complete content in the browser, but crawlers, link previews, no-JavaScript clients, and error monitors first receive an HTTP status and initial HTML. When every path returns the same empty index.html and title and canonical appear only after a client effect, search and sharing signals can diverge. Prerender stable public routes at build time; do not static-render dashboards or personal data. As of 2026-07-29, Google's JavaScript SEO guidance still emphasizes crawlable links, meaningful status codes, and metadata. Its sitemap guide asks for fully qualified canonical URLs and says lastmod should reflect a real significant update. React's server APIs render component trees to HTML, while a Vite production build provides the asset foundation. Implementation steps Create one route manifest containing path, route key, indexability, title and description, canonical, language, data source, and update time. Navigation, prerendering, sitemap generation, and smoke tests all consume it, avoiding four hand-maintained route lists. export const publicRoutes = [ { path: "/", indexable: true, key: "home" }, { path: "/articles", indexable: true, key: "articles" }, { path: "/contact", indexable: true, key: "contact" }, ] as const Run a prerender script after vite build . Create an isolated render context for each indexable route, await necessary data, and write dist/<path>/index.html . Use a suitable react-dom/server static or streaming API, or a headless browser, with deterministic readiness and timeout. Any route failure should fail the build rather than ship a partial set. Each head contains a unique title, description, self-canonical, Open Graph URL and locale, and applicable structured data. Serialize JSON-LD safely rather than interpolating untrusted HTML. The primary H1 and content are in initial HTML, and internal links render normal href values. Split hosting fallback behavior: a prerendered route serves its HTML; hashed assets and APIs behave normally; client-only authenticated routes may receive the SPA shell; an unknown public URL returns a real 404 page and status. A global 200 rewrite to index turns typos into soft 404s. Generate the sitemap from the route and content source, listing only indexable self-canonical absolute URLs. Derive lastmod from a real content update, not the build time. Production robots references the production sitemap. Preview and staging remain noindex or access-controlled and never emit production canonical inconsistently. Build a verifier that reads every HTML file and checks title, description, canonical, H1, language, and no placeholders. Parse the sitemap and require an exact URL set match with no preview host, duplicate, or non-200 URL. Test robots, 404, and referenced assets too. Failure and recovery A hanging prerender often waits on networkidle , an open timer or socket, or route data that never leaves loading. Wait for an explicit app-ready marker, set a per-route timeout, and list failed paths. Do not ignore them and deploy. When every page has home metadata, head state may persist between routes or the renderer may remain in home context. Create a fresh render context per route, add canonical and title uniqueness tests, and roll back to the last trustworthy artifact. When the sitemap contains a draft or 404, repair the manifest or status filter and regenerate it. Do not manually delete one entry while leaving the generator wrong. If every build changes lastmod , switch to content timestamps. When the custom domain remains stale, compare deployment and cache-busted custom responses and hashes. Purge only after proving a cache mismatch, then reverify. Verification commands npm run build rg -n '<title>|canonical|<h1|application/ld\\+json' dist curl -sS https://example.com/sitemap.xml | head curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/not-a-real-page Parse initial HTML for every route rather than using browser Elements as the only evidence. Then use domcontentloaded to prove hydration, navigation, and interaction have no errors. Verify status, canonical, sitemap, robots, 404, and cache-busted content on the production custom domain. Primary sources Google JavaScript SEO basics Google Build and submit a sitemap React Server APIs Vite Building for Production Internal links Browse technical articles for multilingual SEO and Cloudflare debugging. Implement route-driven prerendering through the course catalog . Share a problematic canonical URL through the contact page .