コンテンツにスキップ
まだあたたかい

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.

Tutorials 3 分で読めます

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:

Terminal window
pagefind --site dist --output-subdir _pagefind

Pagefind 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.

KeyAction
/Open the search modal
Cmd+K / Ctrl+KOpen the search modal
/ Navigate results
EnterOpen highlighted result
EscClose 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.

SymptomFix
”Search index not available”Run bun run build once. The index lives in dist/_pagefind/.
Search works in preview but not devExpected — Pagefind only runs after astro build.
Old content still showing upRe-run bun run build. Or run bun run pagefind alone if dist/ is fresh.
Sidebar text appears in resultsConfirm the regions carry data-pagefind-ignore.