Case studies
2026·Open Circle

Valibot and Formisch docs served from cache, not from a function

We rebuilt the Valibot and Formisch documentation as fully static Qwik SSG: 1,100+ pre-rendered pages, no function on the request path, and preferences applied before first paint so nothing flashes or shifts. Both sites now answer from an edge cache instead of rendering per visit.

Talk to an expert
Valibot and Formisch docs served from cache, not from a function
See it in action
Tech stack
QwikTypeScriptViteStatic Site GenerationSharpCloudflare
~80 msEdge TTFB on a cache hit
0Functions on the request path
1,100+Pages pre-rendered, both sites

The cost sat on the request path

Valibot and Formisch are TypeScript libraries with large reference sections: 819 pages on valibot.dev and 291 on formisch.dev. Both docs sites rendered on Vercel Edge at request time. Qwik resumes instead of hydrating, so the browser side was already lean, but every page view still went through a function. On top of that, reader preferences (theme, chapters, framework) lived in httpOnly cookies read by routeLoader$, so the HTML was tied to a specific request and could not simply be handed to a CDN.

Pre-render everything, keep the SPA

Swapping the Vercel Edge adapter for the Qwik Router SSG adapter turns every route into plain HTML plus a per-route q-data.json. The HTML is what the browser gets on first load, and q-data.json is what client-side navigation fetches afterwards. Nothing changes for the reader, except that no request wakes a function any more.

The hard part: preferences without a flash

Moving a cookie-backed setting into the browser has an obvious implementation that is wrong. Reading localStorage inside useVisibleTask$ runs after first paint, so the page renders the default and then snaps to the stored value. For the theme that is a flash of colour. For the chapters toggle it is a real layout shift, because the setting changes max-width, the side column, and the page padding.

The fix is to decide before paint. A blocking inline script in the head reads localStorage and toggles a class on the html element, and the layout keys off that class, never off a post-hydration signal. In Tailwind v4 the visible state is the unprefixed base and a custom no-chapters variant overrides it, the same model as dark. The chapters aside is always rendered and hidden through the variant, so hiding it reflows nothing. Signals still exist, but only to label the toggle button.

OG images moved into the build

The old dynamic /og-image route drew a card per request with @vercel/og. Now one PNG per route is generated at build time and piped through sharp with palette quantisation, since the cards use a handful of flat colours. The head points straight at /og/.png, so a crawler asking for a preview gets a file off the CDN instead of waking a function.

Cache headers matched to the content

Hashed output under /build, /assets and /fonts gets a year and immutable. q-data.json deliberately does not: the URL is stable but the content changes on every deploy, because it references build symbol hashes. It gets a short browser max-age, a long shared max-age, and a week of stale-while-revalidate. Navigation stays instant, and no browser holds a payload pointing at symbols the deploy removed.

One patch to stop losing pages

Qwik SSG appended a trailing slash to anything that looked like a route, including paths ending in a file extension. Some pages never made it into the build at all, and dotted paths such as .md and .png broke. A small patch narrows the check to known extensions, and the missing routes came back in the build output.

Output that any CDN can serve

The build leaves HTML, JSON, fonts and images, with no runtime tied to a particular adapter, so the same output can be served by any CDN and the hosting decision stays open. Both sites currently run on Cloudflare and return the first byte in roughly 80 ms from Europe on a cache hit.

Part of our OSS Partner Program

This was the first delivery under our OSS Partner Program. The point is to take recurring infrastructure and maintenance work off open-source teams, so their time and funding go back into the libraries the rest of the ecosystem depends on.

Frequently Asked Questions

Docs rendering on every request? Let's pre-render them.

Talk to an expert
Zanek

Can't keep up with changes in AI world?

Let us do the heavy lifting. Every week we distill the most important AI developments into a focused 5-minute briefing - so you stay ahead without the noise.

Find out more
Weekly AIonline