Search with Pagefind: instant, static, and fully offline
How the bundled Pagefind search works — modal vs full-page UX, indexing rules, keyboard shortcuts, locale filtering and customisation hooks.
This site has instant, fully-static search powered by
Pagefind. No third-party service, no server,
no API key — Pagefind crawls the built dist/ directory after
astro build and writes a small index plus a tiny client bundle into
dist/_pagefind/.
Try it now: press / or Cmd+K (Ctrl+K on Windows/Linux) to open the modal.
A button lives in the top bar (and on mobile in the sidebar). Opening
it lazy-loads
/_pagefind/pagefind.js — until you open the modal, zero
search code is shipped.
Highlights:
- Auto-focused input
- Arrow keys navigate, Enter follows, Esc closes
- Result list shows title, breadcrumb, and excerpt
- Filtered to the current locale
For deep linking, sharing, or paginated browsing, the theme also ships a
full-page search at /search/ (and /fr/search/). It is
identical to the modal but takes the entire prose column.
bun run build runs Astro, then runs:
pagefind --site dist --output-subdir _pagefindPagefind crawls every static .html page Astro emitted and reads the
content of the <main> element by default. The theme’s
BaseLayout.astro wraps the content
area in <main id="main">, so this just works.
To exclude a region from the index, mark it with data-pagefind-ignore:
<aside data-pagefind-ignore> <p>Marketing CTA — not useful in search results.</p></aside>The theme already excludes the sidebar, footer, and right panel so they don’t pollute search hits with navigation text.
If <main> is too broad, mark the real content with
data-pagefind-body:
<article data-pagefind-body> <!-- only this is indexed --></article>Pagefind also supports filters (e.g. by tag, category, locale) via
data-pagefind-filter:
<article data-pagefind-filter="category:Tutorials"></article>The default UI does not display filter chips, but the headless API exposes them — see “Customisation” below.
| Key | Action |
|---|---|
| / | Open the search modal |
| Cmd+K / Ctrl+K | Open the search modal |
| ↑ / ↓ | Navigate results |
| Enter | Open highlighted result |
| Esc | Close the modal |
The shortcuts are wired in
src/components/islands/SearchButton.astro.
Search is locale-aware. The theme reads the active locale from the
<html lang> attribute, then constrains the Pagefind query to that
locale’s index. French readers do not see English hits and vice-versa.
Translation strings (placeholder text, “no results”, keyboard hints)
live in src/i18n/ui.ts under the search.* keys.
Pagefind is purpose-built for static sites. For a typical 50-post bilingual blog you can expect:
- ~50 kB of compressed JS shipped to the client (only on modal open)
- ~150 kB of compressed index data, fetched lazily and cached
- ~5–20 ms average query latency (in-browser, no network round-trip)
The modal does not load anything until you click it, so the cost on the home page is zero.
Open
src/components/islands/SearchButton.astro
to:
- Change the keyboard shortcut.
- Toggle showing images / sub-results.
- Add a filter UI (the headless API exposes
pagefind.filters()). - Replace the result row markup with your own template.
The component uses the headless Pagefind API (pagefind.js) — not
the bundled pagefind-ui.js — so you have full control over the
layout and styling. No extra Pagefind CSS is shipped; everything is
styled with Tailwind.
| Symptom | Fix |
|---|---|
| ”Search index not available” | Run bun run build once. The index lives in dist/_pagefind/. |
Search works in preview but not dev | Expected — Pagefind only runs after astro build. |
| Old content still showing up | Re-run bun run build. Or run bun run pagefind alone if dist/ is fresh. |
| Sidebar text appears in results | Confirm the regions carry data-pagefind-ignore. |