Skip to content
Dawit.

performance

Shipping search without a server

Part three of building this blog — full-text search from a static index with Pagefind, costing nothing at rest and loading only on demand.

· 3 min read

Contents

Parts one and two built the content model and the discovery layer. The remaining reader-facing gap was search — the feature that traditionally ends the “no server” streak. It doesn’t have to. The claim of this part: for a content site, a pre-built static search index beats a search backend on every axis that matters here, and it costs exactly nothing at rest.

Search is an index, not a service

The mental unlock is that search has two phases, and only one of them needs to happen when the reader types. Indexing — tokenizing text, building the lookup structures — can happen at build time, because a static site’s content only changes at build time. Running an always-on service to re-answer a question whose inputs change weekly is the same mistake as rendering the same article on every request.

Pagefind implements this cleanly. After the site builds, it crawls the finished HTML and writes a fragmented index alongside the static files. The fragments are the clever part: the browser fetches only the pieces relevant to the query — a few tens of kilobytes for a typical search — rather than downloading the whole index. No server, no per-query cost, no third-party service holding the feature hostage.

Paying for search only when it’s used

The performance rule for a feature like this: readers who don’t use it must not pay for it. Most article visits never touch search, so the page-weight budget should not carry it by default.

Implementation is a modal that lazy-loads everything on first open. Until then, search costs a button. After first use, the library is cached and subsequent queries are instant. The search UI itself is the page’s one substantial piece of JavaScript, mounted idle so it never competes with rendering the article — and with a keyboard shortcut, because returning readers are the ones who search.

Two details earn their keep. Only the article body gets indexed — navigation and footer text are excluded, so searching “archive” doesn’t return every page on the site. And the no-JavaScript path still works: the search page renders the full article list, so content remains reachable when the enhancement doesn’t load.

The empty state is interface copy

A search box with no results is a fork: “No results” is a dead end, while “try a topic name or a phrase from an article” is a direction. On a small site this matters more, not less — queries miss more often against a few dozen articles than against a million, so the recovery path is the feature. Same principle as a 404 page: never leave a reader nowhere.

The series so far

Three parts in, the pattern behind every decision is visible: move work to build time, keep the reader’s path free of moving parts. Validation happens at build. Metadata is generated at build. Search is indexed at build. What ships is inert files — fast because there is nothing left to be slow, reliable because there is nothing left to fail.

The series continues as the site grows. Next candidates: the newsletter pipeline, and what a year of Core Web Vitals field data says about these choices. If you want the next part when it lands, subscribe — or watch the feed.

One essay on building for the web, most weeks

No digests, no product updates — the same articles that appear here, in your inbox. Unsubscribe with one click, any time. Or take the RSS feed instead.

Double opt-in — you'll get a confirmation email first. Your address is used for nothing else.