How search works on this site
March 2026
This site is a plain Jekyll project: Markdown and HTML turn into static files, and there is no server-side search API. Anything that feels like search has to ship with the static assets or run in the browser. Here is the setup I use and how the pieces fit together.
Build-time indexing with Pagefind
After Jekyll writes the site to _site/, the build runs Pagefind against that output folder. Pagefind walks the built HTML, extracts text from each page, and writes a compressed search bundle under pagefind/ (WASM plus chunked indexes). That bundle is what the browser loads when you open search.
So the index is always consistent with whatever was last built: new post, new page, or typo fix—all of it is reflected only after a rebuild (and redeploy, for GitHub Pages).
What you see in the UI
On the home page, the sidebar embeds Pagefind’s default UI: you type a query, it runs locally against the index, and results link back to URLs on this domain. Nothing is sent to a third-party search service.
The concept graph at /graph/ uses the same Pagefind JavaScript API, not the widget. It runs a filter-only search (no keyword) so every indexed page is loaded, then derives co-occurring terms from titles and snippets to draw an exploratory graph. That is a small custom layer on top of the same index the search box uses.
Local and CI builds
The docs/ folder includes an npm script that runs Jekyll and then Pagefind. If you clone the repo and build without that second step, the pagefind/ assets will be missing and search (and the graph) will show a friendly error until you run the full pipeline.
Why not something else?
Hosted search APIs, Elasticsearch, or a database would work for larger or more dynamic sites. For a personal blog with a few dozen pages, a client-side index keeps hosting simple, avoids credentials and backends, and stays fast enough on phones and laptops.
If this site ever outgrows that model, the content would still be static files; only the discovery layer would change.