Articles

Add Full-Text Search to Any Static Site with Pagefind

Static sites don't need a search server. Learn how Pagefind indexes your HTML at build time and adds fast, client-side full-text search for free.

Takina Takina · · Updated · 5 min read
Search interface illustration

Pagefind is a static search library: after your site generator finishes, it crawls the HTML in your output folder, writes a compressed search index next to it, and gives you a small JavaScript UI that queries that index entirely in the browser. No search server, no hosted API, no keys to rotate — the index is just files, served from the same CDN as the rest of your site.

This blog’s search runs on Pagefind, so the setup below is the setup in production here. The official docs live at pagefind.app; everything that follows works with a stock install.

Step 1: index your built site

Pagefind operates on finished HTML, so it runs after the build, pointed at your output directory:

npx pagefind --site dist

For anything beyond a one-off test, chain it into your build script so every deploy reindexes automatically:

{
  "scripts": {
    "build": "astro build && pagefind --site dist"
  }
}

This writes a pagefind/ folder into dist/ containing the index fragments and the UI assets. Deploy that folder with everything else and you’re done with infrastructure — there is none.

Step 2: drop in the search UI

On your search page, load the prebuilt UI, give it a target element, and initialize it:

<link rel="stylesheet" href="/pagefind/pagefind-ui.css" />
<script src="/pagefind/pagefind-ui.js"></script>
<div id="search"></div>
<script>
  window.addEventListener("DOMContentLoaded", () => {
    new PagefindUI({ element: "#search", showSubResults: true });
  });
</script>

That’s a working search box with results and excerpts. The showSubResults option surfaces matching sections within a page, keyed off its headings.

One catch: the /pagefind/ assets only exist after a real build, so search won’t work in the dev server. Test against a production build instead:

npm run build && npm run preview

How does it stay fast?

This is the clever part. Pagefind doesn’t ship one big index — it splits the index into many small fragments and loads them lazily. When a visitor types a query, the browser fetches a tiny entry file plus only the fragments relevant to those search terms. The rest of the index never leaves the CDN. The project’s long-standing design target is a full search on a 10,000-page site costing a total network payload in the low hundreds of kilobytes, library included.

That’s why Pagefind scales where naive client-side search — load one giant JSON blob, scan it — falls over. The index grows with your site; the per-query download barely does. Indexing itself is quick, too: it’s a single CLI pass over your HTML, so it slots into any CI pipeline without extra configuration.

Step 3: scope what gets indexed

By default Pagefind indexes each page’s whole body, which means navigation, footers, and sidebars pollute your results. Fix that by marking your real content:

<article data-pagefind-body>
  <!-- your post content -->
</article>

Two behaviors worth knowing:

  • Once data-pagefind-body appears anywhere on your site, pages without it are dropped from the index entirely — a tidy way to exclude tag pages and pagination in one move.
  • data-pagefind-ignore excludes an element and its children inside an indexed region — a table of contents, a related-posts widget, a comments section.

Step 4: filters and metadata

Filters let readers narrow results by facets you define. Tag any element inside the indexed region:

<span data-pagefind-filter="tag">Astro</span>
<span data-pagefind-filter="author">Takina</span>

The default UI picks these up automatically and renders a filter panel beside the results.

Metadata controls what each result displays. Pagefind auto-detects a page’s title and leading image, and data-pagefind-meta overrides or extends that:

<h1 data-pagefind-meta="title">Add Search to a Static Site</h1>
<img data-pagefind-meta="image[src]" src="/covers/search.png" alt="Search UI" />
<span data-pagefind-meta="date">2026-06-16</span>

Step 5: make it match your site

The default UI is themed with CSS custom properties, so matching your brand takes a few lines rather than a fork:

:root {
  --pagefind-ui-scale: 0.9;
  --pagefind-ui-primary: #e0244c;
  --pagefind-ui-text: #1f2933;
  --pagefind-ui-background: #ffffff;
  --pagefind-ui-border: #d9dee6;
  --pagefind-ui-border-radius: 8px;
  --pagefind-ui-font: inherit;
}

If you need full control over markup, skip pagefind-ui.js and call Pagefind’s lower-level JavaScript API to run queries and render results yourself.

Using it with Astro

Nothing above is Astro-specific — Pagefind never touches your source, only the built HTML, so the same recipe covers Hugo, Eleventy, or Jekyll, and it survives framework upgrades like Astro 6 untouched. On an Astro site the flow is: astro build produces dist/, Pagefind indexes dist/, and a /search/ page includes the UI snippet from step 2. One convenient shortcut: if a shared layout wraps every post in an <article>, adding data-pagefind-body there once scopes indexing for the entire archive. If you deploy to Cloudflare Pages, point the build command at the chained script from step 1 and the index regenerates on every push.

Hosted services like Algolia solve search by running it for you: your build pushes records to their servers, and every keystroke calls their API. That’s the right trade for some products — and overkill for most static sites.

PagefindHosted search (e.g., Algolia)
InfrastructureNone — static filesExternal service, API keys
CostFree at any traffic levelFree tier, then usage-based
Index updatesEvery build, automaticallyPush records via API or crawler
Where queries runIn the visitor’s browserOn the provider’s servers
Advanced featuresFull-text, filters, metadataTypo tolerance, analytics, ranking controls
PrivacyQueries never leave the browserQueries hit a third party

Choose hosted search when you need search-as-a-product: analytics dashboards, merchandising, tunable relevance. For a content site measured in hundreds or a few thousand pages, Pagefind delivers the same reader experience with zero moving parts.

The takeaway

Pagefind turns search into a build step: index the HTML you already generate, serve the result as static files, and download only the index fragments a query actually needs. Scope the index with data-pagefind-body, enrich results with filters and metadata, and restyle the UI with a handful of CSS variables. There’s no server to run and nothing to pay for — which, for a static site, is exactly the point.

Takina Takina · · 4 min read

requestIdleCallback Explained

requestIdleCallback runs low-priority JavaScript when the browser is idle, without blocking rendering, input, or the main thread.

#JavaScript #Performance #Web Development
Takina Takina · · 5 min read

Finding and Fixing Memory Leaks in JavaScript

A JavaScript memory leak happens when a reference outlives its usefulness and the garbage collector can't reclaim it. Common causes and how to find them.

#JavaScript #Web Development #Performance