Repository navigation
Fix site indexability with per-route prerendering - #5
Merged
Merged
Conversation
One index.html answered every URL, and it hardcoded a canonical of "/", so /usage and /playground told crawlers they were duplicates of the homepage. The body was an empty div, leaving nothing for a crawler that does not run JavaScript. Each route is now a real HTML file with its own head and its own rendered markup.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Repository owner
locked as resolved and limited conversation to collaborators
Aug 17, 2026
This branch was successfully deployed
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Every URL was served one
index.htmlwhose canonical was hardcoded to/, so/usageand/playgrounddeclared themselves duplicates of the homepage. The body was an empty div, and there was norobots.txtor sitemap.Each route is now prerendered to its own HTML file with its own head and rendered markup.
src/lib/seo.tslists every page;src/lib/head.tsdescribes the head once, used at build time and on client navigationbun run buildadds an SSR build plusscripts/prerender.ts, writing one file per route andsitemap.xmlrobots.txt, JSON-LD on the homepage,noindexon the 404Adding a route now requires adding it to
PAGES, or it 404s on a direct hit.Also adds
CLAUDE.md, and moves the//comment out ofoverrides— npm reads every key there as a package name and refuses to run on it.Summary by cubic
Prerenders each route to its own HTML and head so crawlers can index pages. Old: one
index.htmlfor every URL with canonical “/” and an empty body; New: each route serves rendered markup, its own canonical/title/OG/Twitter, emits a sitemap, and unknown URLs return a real 404 withnoindex.apps/webrunsvite build,vite build --ssr src/entry-server.tsx --outDir dist-ssr, thenbun scripts/prerender.ts; outputsdistanddist-ssr..gitignore,turbo.json,tsconfig.json, andknip.jsonupdated.src/lib/seo.tsdefinesPAGES;src/lib/head.tscentralizes tags and the client applies them on SPA navigation. JSON‑LD on the homepage;robots.txtadded; 404 servesnoindex.cleanUrls: true,trailingSlash: false. Rootvercel.jsonis the source of truth.PAGESinapps/web/src/lib/seo.ts, or direct hits 404 and the page is excluded from the sitemap./robots.txtand/sitemap.xmlload,/usageand/playgroundhave correct canonicals/titles, and unknown paths return a 404 withnoindex.overridesintooverridesCommentin the rootpackage.jsonso npm can read the workspace.Written for commit 0db97b0. Summary will update on new commits.