DEV Community

MetCoreB
MetCoreB

Posted on

My SPA was invisible to Google, and the fix broke on Cloudflare's build

I built kitdecoder.com, a set of browser-based dev tools, as a plain JS
single-page app. A few weeks in I fetched my own homepage the way a crawler would
and got back the and an empty

. Headings, FAQ and
structured data only existed after JavaScript ran, so as far as Google was
concerned the site was blank.

The fix is prerendering: visit every route at build time and save the resulting
HTML. I wrote a small Playwright script that serves the build locally, opens each
of the ~40 routes, waits for the h1 to appear, and writes page.content() to
dist//index.html. It worked on the first local run.

Then I put it in the Cloudflare build command and it failed:

error while loading shared libraries: libatk-1.0...

npx playwright install gets you the browser, but Chromium also needs system
libraries that a minimal build container doesn't ship, and I didn't find a way to
install them there. So I stopped trying to make CI do it. The build, Playwright
step included, runs on my machine, and I deploy the finished dist/ with wrangler:

npm run build && npx wrangler deploy

I lost push-to-deploy. In exchange, the deploy can't break on a library I don't
control.

Two other things bit me along the way:

  1. The prerender step was executing my AdSense script, so every page load during the build fired a real ad request, and one of the saved pages ended up with an ad iframe baked into the static HTML. I now block Google's ad domains with page.route() before navigating.
  2. page.content() saved the preview server's address into the asset links (http://127.0.0.1:4173/assets/...), which is broken in production. I rewrite those to relative URLs before writing each file.

If you've prerendered an SPA yourself, did you hit the same things?

kitdecoder.com

Top comments (0)