/ astro-integrations / How to Use Pagefind with Astro: Complete Guide
astro-integrations 4 min read

How to Use Pagefind with Astro: Complete Guide

Step-by-step guide to integrating Pagefind with your Astro website. Installation, configuration, and best practices.

Pagefind is a static search library that indexes your site at build time and runs entirely in the browser. No server, no API key, no monthly bill. It is the perfect search solution for Astro because both tools embrace the static-first approach. Your search index is just a set of files shipped alongside your site.

Prerequisites

  • Node.js 18+
  • An Astro project (npm create astro@latest)
  • A site with some content (blog posts, docs, or pages to search through)

Installation

Install Pagefind as a dev dependency:

npm install -D pagefind

Then add a postbuild script to your package.json:

{
  "scripts": {
    "build": "astro build",
    "postbuild": "pagefind --site dist"
  }
}

This runs Pagefind after every Astro build, indexing the HTML files in your dist directory.

Configuration

Pagefind works with zero configuration out of the box. It crawls all your HTML pages and indexes the text content. If you want to customize its behavior, create a pagefind.yml in your project root:

# pagefind.yml
site: dist
glob: "**/*.html"
exclude_selectors:
  - "nav"
  - "footer"
  - ".sidebar"

The exclude_selectors option tells Pagefind to skip navigation, footer, and sidebar content so search results only contain your main content.

Basic Usage

Add the Pagefind UI to your site. Create a search page or component:

---
// src/pages/search.astro
import BaseLayout from "../layouts/BaseLayout.astro";
---

<BaseLayout title="Search">
  <main class="max-w-3xl mx-auto px-4 py-12">
    <h1 class="text-3xl font-bold mb-8">Search</h1>
    <div id="search"></div>
  </main>
</BaseLayout>

<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
<script is:inline>
  import("/pagefind/pagefind-ui.js").then((module) => {
    new module.PagefindUI({
      element: "#search",
      showSubResults: true,
      showImages: false,
    });
  });
</script>

After running npm run build, Pagefind generates its index and UI assets in the dist/pagefind/ directory. The search widget loads lazily and only fetches index chunks as the user types.

Controlling What Gets Indexed

Use data-pagefind-body to tell Pagefind which part of your pages to index:

---
// src/layouts/BlogPost.astro
const { title, description } = Astro.props;
---

<html>
  <body>
    <nav><!-- not indexed --></nav>

    <article data-pagefind-body>
      <h1>{title}</h1>
      <slot />
    </article>

    <footer><!-- not indexed --></footer>
  </body>
</html>

When you add data-pagefind-body to any element, Pagefind switches from indexing the entire page to only indexing elements with that attribute.

You can also add metadata for filtering:

<article
  data-pagefind-body
  data-pagefind-meta="category:tutorials"
  data-pagefind-filter="tag:astro"
>
  <h1>{title}</h1>
  <slot />
</article>

Using the JavaScript API

For a custom search UI (not the default widget), use the Pagefind JavaScript API directly:

<input type="text" id="search-input" placeholder="Search..." />
<div id="results"></div>

<script is:inline>
  let pagefind;

  async function initSearch() {
    pagefind = await import("/pagefind/pagefind.js");
    await pagefind.init();
  }

  initSearch();

  document.getElementById("search-input").addEventListener("input", async (e) => {
    const query = e.target.value;
    const container = document.getElementById("results");
    if (!query) {
      container.textContent = "";
      return;
    }

    const search = await pagefind.search(query);
    const results = await Promise.all(
      search.results.slice(0, 10).map((r) => r.data())
    );

    container.textContent = "";
    results.forEach((r) => {
      const link = document.createElement("a");
      link.href = r.url;
      const heading = document.createElement("h3");
      heading.textContent = r.meta.title;
      const excerpt = document.createElement("p");
      excerpt.textContent = r.excerpt;
      link.appendChild(heading);
      link.appendChild(excerpt);
      container.appendChild(link);
    });
  });
</script>

Production Tips

  1. Use data-pagefind-body on your content wrapper. Without it, Pagefind indexes everything including headers, footers, and navigation. Narrowing the scope gives much better search results.

  2. Add data-pagefind-ignore to boilerplate. For elements inside your indexed body that should be skipped (like table of contents or share buttons), add data-pagefind-ignore.

  3. The index is lazy-loaded. Pagefind only loads a few KB initially and fetches index chunks as needed. Your page load performance is not affected by having thousands of pages indexed.

  4. Test search during development. Run npm run build first, then npm run preview to test search locally. The search index only exists after a build.

  5. Use multilingual support. Pagefind automatically detects the lang attribute on your HTML element and creates separate indexes per language. No extra configuration needed for multilingual Astro sites.

Alternatives to Consider

  • Algolia if you need hosted search with advanced features like typo tolerance, faceting, and analytics. More powerful but costs money at scale.
  • Meilisearch if you want a self-hosted search engine with a great developer experience. Requires a server to run.
  • Orama if you want a full-text search engine that runs in the browser with vector search capabilities.

Wrapping Up

Pagefind is the best search solution for most Astro sites. It is free, requires no server, builds in seconds, and delivers fast, relevant results. Add it to your build pipeline once and forget about it.