Skip to content

Prerender SPA at build time so LLM/search crawlers see the full page#2

Merged
ampedraszewska merged 1 commit into
mainfrom
geo-prerender
Apr 29, 2026
Merged

Prerender SPA at build time so LLM/search crawlers see the full page#2
ampedraszewska merged 1 commit into
mainfrom
geo-prerender

Conversation

@ampedraszewska

Copy link
Copy Markdown
Owner

Why

Builds on PR #1 (meta + JSON-LD + llms.txt). PR #1 gave LLM and search crawlers rich metadata in the initial HTML response — but the actual page content (team, thesis, portfolio news ticker) still lived behind the React bundle. Crawlers that don't reliably run JS (GPTBot, ClaudeBot, PerplexityBot, and the indexing pass for several others) only saw <div id="root"></div> for the body.

This PR finishes the fix by prerendering the page at build time: after vite build, a Puppeteer headless browser loads the built SPA, captures the fully-rendered DOM, and writes it back to dist/index.html (and dist/404.html for the GitHub Pages SPA fallback).

What that gives us

LLM crawlers and search engines now receive the complete Hero component as static HTML:

  • The full headline and sub-thesis ("early-stage fund backing Poland's boldest tech founders…")
  • All three partners with names, roles, and LinkedIn links (Aleksandra Pedraszewska, Karolina Kukielka, Zuzanna Brzosko)
  • The news ticker carrying the Replenit pre-seed announcement
  • Locations, contact, partner logos

The React bundle still hydrates and runs client-side for interactive features (mouse-tracking effect, mobile menu).

What changed

  • scripts/prerender.mjs (new): post-build script that uses Vite's preview() API and Puppeteer
  • package.json:
    • build is now vite build && node scripts/prerender.mjs
    • build:nossg added as an escape hatch (just vite build, in case the prerender step ever needs to be skipped)
    • puppeteer added to devDependencies

No changes to React components, routing, or build configuration beyond the script orchestration.

Local test

$ npm run build
> vite build && node scripts/prerender.mjs

vite v5.4.10 building for production...
✓ 1665 modules transformed.
dist/index.html                   4.96 kB │ gzip:  1.47 kB
✓ built in 2.10s
Created 404.html for GitHub Pages SPA fallback
[prerender] starting Vite preview server on port 4173
[prerender] rendering http://127.0.0.1:4173/
[prerender] wrote /private/tmp/vastpoint-work/vastpoint-test/dist/index.html (25730 bytes)
[prerender] wrote dist/404.html (mirror of index.html)
[prerender] done

$ grep -oE "Aleksandra Pedraszewska|Karolina Kukielka|Zuzanna Brzosko|Replenit" dist/index.html | sort -u
Aleksandra Pedraszewska
Karolina Kukielka
Replenit
Zuzanna Brzosko

Verify after merge

GitHub Actions runs the same npm run build. About 30-60 sec extra build time for Chromium download (cached on subsequent runs).

After deploy:

curl -A "GPTBot/1.0" https://vastpoint.vc/ | grep -E "Pedraszewska|Replenit"

Should print both. Currently (after PR #1 only) prints neither.

What is not in this PR

  • No content changes
  • No design changes
  • No routing changes
  • The CI build will be slightly slower because Puppeteer downloads Chromium. We can tighten this later with puppeteer-core plus setup-chrome, but the simple path works first.

Builds on PR #1 (meta + JSON-LD + llms.txt). This finishes the SEO/GEO
fix: after `vite build`, a Puppeteer headless browser loads the built
SPA, captures the fully-rendered DOM, and writes it back to
dist/index.html (and dist/404.html for the GitHub Pages SPA fallback).

The result: LLM crawlers (GPTBot, ClaudeBot, PerplexityBot) and search
engines that do not reliably execute JS now get the complete Hero
component as static HTML — team, thesis, news ticker with the Replenit
announcement, contact info, partner logos. The React bundle still
hydrates and runs client-side for interactive features (mouse-tracking
effect, mobile menu).

Local build measurement:
- dist/index.html before: 4.96 kB (essentially empty body)
- dist/index.html after:  25.7 kB (full rendered Hero)

Changes:
- scripts/prerender.mjs (new): post-build script using Vite preview
  server plus Puppeteer to snapshot rendered HTML
- package.json: build = "vite build && node scripts/prerender.mjs",
  added build:nossg as escape hatch, puppeteer as devDependency

No changes to React components or routing.

Tests:
- npm install completes (puppeteer pulls Chromium ~150 MB)
- npm run build completes successfully
- dist/index.html grep confirms team names plus Replenit present in markup
- dist/404.html mirrors index.html (SPA fallback preserved)

After merge, GitHub Actions runs the same build pipeline. Verify with:
    curl -A "GPTBot/1.0" https://vastpoint.vc/ | grep "Pedraszewska"
@ampedraszewska ampedraszewska merged commit cb90245 into main Apr 29, 2026
2 checks passed
@ampedraszewska ampedraszewska deleted the geo-prerender branch April 29, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant