Add proper language tags, sitemap, robots.txt to the site#45
Merged
Conversation
Serif "MR" monogram on the accent-purple background, generated at SVG plus 16/32px PNG, apple-touch (180px), and legacy .ico. Wired into every page via the root links export. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Derive the sitemap from the route config rather than a hand-written file: a `buildEnd` hook writes build/client/sitemap.xml from a single PAGES map in seo-links.ts, and the `prerender` hook cross-checks getStaticPaths() so a prerendered page that's missing from both the sitemap and the exclude list warns at build time. Centralize SITE_URL and the EN<->DE page pairing in seo-links.ts so canonical, hreflang, and the sitemap can't drift apart; the route link functions now take a PAGES entry. Remove the now-redundant public/sitemap.xml. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move sitemap generation from the buildEnd hook into a resource route (app/routes/sitemap.tsx) whose loader returns renderSitemap() as application/xml. It still prerenders to build/client/sitemap.xml and now also serves live under `npm run dev`, at the cost of a sitemap.xml.data sidecar and a tiny client chunk. The prerender hook keeps the getStaticPaths() cross-check but now throws instead of warning, so a prerendered page missing from both PAGES and the exclude list fails the build. /sitemap.xml is added to the exclude list since the resource route makes it a discovered path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Open Graph/Twitter meta in the home routes hardcoded https://ruehe.me, duplicating the domain that canonical/hreflang already derive from SITE_URL. og:url in particular must match the canonical URL; build both from SITE_URL + PAGES.home so a domain change can't leave them out of sync. Output is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
What & why
Fixes the bilingual SEO plumbing: the EN (
/) and DE (/de) trees didn't reference each other, so Google treated them as unrelated pages. Adds reciprocalhreflang+canonicallinks, a generated sitemap, and a robots.txt.Changes
app/seo-links.ts— single source of truth for SEO URLs:SITE_URLand aPAGESmap of EN↔DE equivalents (/↔/de,/en/imprint↔/de/impressum).bilingualLinks(page, lang)returnsrel="canonical"+rel="alternate"hreflanglinks foren,de, andx-default(defaults to the English URL).renderSitemap()/sitemapPaths()derive the sitemap and its URL list from the samePAGESmap, so canonical, hreflang, and the sitemap can't drift apart.Route.LinksFunction(home,home-de,imprint,imprint-de), each passing itsPAGESentry.og:urlandog:imagenow derive fromSITE_URL+PAGES.homeinstead of hardcodinghttps://ruehe.me, soog:urlstays in sync with the canonical URL.app/routes/sitemap.tsx— a resource route (loader only) that returnsrenderSitemap()asapplication/xml. It prerenders tobuild/client/sitemap.xmland is also served live undernpm run dev. Registered asroute("sitemap.xml", ...)inroutes.ts.react-router.config.ts—prerenderuses the function form to cross-check route discovery:getStaticPaths()is compared againstPAGES+ an exclude list (/404,/sitemap.xml, and the two standalone app privacy policies), and throws to fail the build if any prerendered page is unaccounted for — so the sitemap can't silently fall out of sync with the routes.public/robots.txt— allow-all +Sitemap:pointer. The previously hand-writtenpublic/sitemap.xmlwas removed.{ lang: "en" }/{ lang: "de" }entries from themeta()arrays inlayout.tsx,layout-de.tsx, and both imprint routes —langisn't a valid meta descriptor;<html lang>is set inroot.tsx.Verification
npm run typecheckandnpm run buildpass.build/client/sitemap.xmlhas the 4 URLs, each withen/de/x-defaultalternates.npm run devserves/sitemap.xml→200,Content-Type: application/xml, 4 URLs.PAGES/exclude list./and/en/imprintemits the correctcanonical+hreflanglinks; on the home pagesog:urlmatchescanonicalandog:imageis unchanged, both now derived fromSITE_URL.Reviewer notes
hrefLang(camelCase) in the static HTML. HTML attribute names are case-insensitive, so crawlers (including Googlebot) parse it identically tohreflang.sitemap.xml.datasidecar and a tiny client chunk alongside the file — accepted in exchange for dev serving. Nothing links to the sidecar and crawlers never request it.PAGESor the exclude list will fail the build (and the deploy) until it's accounted for.SITE_URLis a plain constant; it could readimport.meta.env.VITE_SITE_URLif a staging domain is ever needed.public/robots.txtandpublic/CNAMEstill spell the domain out literally because they're static files, not TS modules.