Deployment
The built site is a folder of static files. There’s no server-side
component, no runtime configuration, and no rebuild required to deploy
to a new path. The relativize pass rewrites every internal URL to be
page-relative, so _site/ works under any URL prefix.
Build
pnpm build runs Eleventy, which writes the site under
_site/ (or whatever --output you pass).
After Eleventy completes, a few more artifacts are produced:
-
_site/symbols.json— the API symbol manifest for the fuzzysort index. See Search → API symbols. -
_site/assets/apidocs/pagefind/— the Pagefind prose index. See Search → Prose search. -
A Markdown sibling for every page (
_site/foo.mdnext to_site/foo/index.html), plus_site/llms.txt(an index) and_site/llms-full.txt(every page concatenated in navigation order) — plain-text renderings for LLM consumption.
These all run from the same eleventy.after hook, so the
output directory is fully self-contained the moment the build
command exits.
Portable URLs
The relativize pass walks every emitted HTML page after the layout and content pipeline are done. For each absolute URL it finds, it computes the page-relative equivalent and rewrites the attribute:
| Page | Original | Rewritten |
|---|---|---|
/ |
/code-blocks/ |
./code-blocks/ |
/code-blocks/ |
/images/ |
../images/ |
/code-blocks/ |
/assets/apidocs/css/apidocs.css |
../assets/apidocs/css/apidocs.css |
The rewrite covers href, src, and
srcset attributes. Absolute URLs to external hosts are left
alone. Protocol-relative URLs (//cdn.example.com/...) are
left alone too — they’re explicitly anchored to the current
scheme.
Verifying the build
Two checks confirm the site is portable:
-
No leading-slash URLs survive in emitted HTML. From the project root (the
--includeskips the.mdsiblings, whose links stay absolute by design):grep -rE 'href="/|src="/' --include='*.html' _site/ && echo "FAILED" || echo "OK" -
Serving the directory from any prefix renders correctly. The quickest local test:
python3 -m http.server -d _site 8000 # visit http://localhost:8000/ — site loads # move _site under some/prefix/ on another server and load # http://that-server/some/prefix/ — site still loads
If both checks pass, the build is portable to any subpath without
rebuilding. That includes deploying preview branches under
https://example.com/pr-1234/ or shipping the site
bundled inside a desktop app at a file:// path.
Common hosts
- Static file servers
-
Nginx, Caddy, or anything that serves a directory. No special configuration — point the document root at
_site/. - GitHub Pages, Cloudflare Pages, Netlify, Vercel
-
Each has its own build command field; set it to
pnpm buildand the publish directory to_site. No further config. - S3 + CloudFront (or equivalent)
-
Sync
_site/to the bucket; serve via the CDN. Setindex.htmlas the default document so directory URLs resolve correctly. - Inside another app
-
Because URLs are relative, you can mount the site at
/docs/of an existing application without rebuilding. Whatever serves your/docs/route just needs to respectindex.htmlat directory paths.
Speculation rules prefetch
The layout includes a <script type="speculationrules">
block that asks the browser to prefetch every same-origin link on
hover/focus, at “moderate” eagerness. On a static host
that means navigations between docs feel near-instantaneous: the next
page is already in cache by the time the click registers.
Combined with cross-document View Transitions, the result is a feel that’s closer to an SPA than a static site — without shipping any router code.