Repository navigation
feat(seo): prerender both languages and fix discoverability - #12
Merged
Merged
Conversation
Static HTML for all four pages now, generated at build time with real router matches instead of an empty `#app` div. `scripts/prerender.mjs` does an SSR build of a new `entry-server.tsx`, renders each route x locale with prerenderToNodeStream (renderToString can't wait for the code-split route components to resolve), and writes real HTML plus per-page title, description, canonical, hreflang, OG/Twitter tags and JSON-LD into dist. main.tsx hydrates that markup instead of replacing it. Getting there took three separate router-level bugs: history entries need the real basepath-prefixed URL (not the bare route path) for links to resolve consistently, canonicalPath's trailing slash has to be stripped before it's used as a route path, and the SSR bundle needs single-file output or route-level code splitting gives each chunk its own copy of LocaleContext. sitemap.xml lists all four URLs with hreflang, robots.txt points at it, manifest.json's name drops its em dash, and the 404 redirect page shows a French message for /fr paths. ```mermaid flowchart LR A[vite build] --> B[dist with empty #app] B --> C[prerender.mjs: SSR each route x locale] C --> D[dist/index.html, /shopping-cart/, /fr/, /fr/shopping-cart/] D --> E[main.tsx hydrates instead of rendering fresh] ``` ## Test plan - pnpm typecheck - pnpm check - pnpm test:coverage, 22 files, 120 tests, thresholds pass - pnpm build, then manually diffed all 4 dist/*/index.html files for real content and correct locale - pnpm test:e2e, including new prerender.spec.ts asserting zero hydration console errors and real markup on all 4 pages Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014BcGPRc3sgUJYKihGRXh5g
6 tasks done
This was referenced Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #10.
Summary
/,/shopping-cart/,/fr/,/fr/shopping-cart/),generated at build time instead of shipping an empty
#appdiv for crawlersscripts/prerender.mjs: SSR build of a newentry-server.tsx, renders each route xlocale with
prerenderToNodeStream(renderToStringcan't wait for the code-split routecomponents to resolve), writes real markup into
distmain.tsxhydrates that markup instead of replacing it<title>, description, canonical, hreflang alternates, OG and Twitter tags,JSON-LD (
WebSitefor home,ItemListandBreadcrumbListfor the shop)sitemap.xmlgenerated with all four URLs and hreflang,robots.txtpoints at it,localized 404 redirect, manifest name cleaned up
Getting a clean hydration took three separate router bugs: the memory history entry needs
the real basepath-prefixed URL for links to render consistently, the canonical path's
trailing slash has to be stripped before it becomes a route path, and the SSR bundle needs
single file output or route-level code splitting gives each chunk its own copy of
LocaleContext, silently wrong locale and all.Test plan
pnpm typecheckpnpm checkpnpm test:coverage, 22 files, 120 tests, thresholds passpnpm build, diffed all 4dist/**/index.htmlfor real content and correct localepnpm test:e2e, newprerender.spec.tsasserts zero hydration console errors andreal markup on all 4 pages
🤖 Generated with Claude Code
https://claude.ai/code/session_014BcGPRc3sgUJYKihGRXh5g