Search crawlers and most LLM crawlers/retrievers (GPTBot, ClaudeBot, PerplexityBot, CCBot)
fetch raw HTML — they don't execute JavaScript. So the real test isn't "does it look right
in a browser," it's "what does curl get back." Verified directly, not assumed.
| Stack | Per-route <title>/description a crawler sees | Trail content in raw HTML (Browse page) | Why |
|---|---|---|---|
| vanilla-static | ✓ Yes — real | ✗ No (0 of 7 trails) | Separate real files give free per-page titles; card content is still filled in by client JS |
| vanilla-spa | ✗ No — one generic title for every route | ✗ No (0 of 7 trails) | Single index.html shell; JS updates the tab title after load, invisible to a crawler |
| site-server | ✓ Yes — real, from front matter | ✓ Yes (7 of 7 trails) | Per-request server renders real title/meta + real trail card HTML before sending |
| react | ✗ No — one generic title for every route | ✗ No (0 of 7 trails) | CSR-only Vite build; same document.title-after-load limitation |
| vue | ✗ No — one generic title for every route | ✗ No (0 of 7 trails) | Same limitation, via router.afterEach instead of a hook |
| angular | ✗ No — one generic title for every route | ✗ No (0 of 7 trails) | Same limitation, via Angular's own Title/Meta services |
document.title/meta-description
update (Angular via its built-in Title/Meta services, Vue via
router meta + afterEach, the others directly). It looks like an
SEO fix, and it does correctly update the browser tab and any social-preview bot that
happens to run JS. But a plain HTTP crawler — including most LLM crawlers — never executes
that code, so it still receives the exact same generic title and empty content it did
before the change. Only vanilla-static (separate real files) and
site-server (a real per-request response) are actually improving what a
crawler receives.
trails.json
before sending the response is a small addition. Doing the same for vanilla-static would
mean adding a build step (a static-site generator) — which gives up its own no-build-step
pitch. Doing it for vanilla-spa/react/vue/angular means true SSR — Next.js, Nuxt, or
Angular Universal — not a small addition, a different architecture.
curl against each stack's production build/server — no
browser, no JS execution, exactly what a crawler experiences.