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
Use
data-pagefind-bodyon your content wrapper. Without it, Pagefind indexes everything including headers, footers, and navigation. Narrowing the scope gives much better search results.Add
data-pagefind-ignoreto boilerplate. For elements inside your indexed body that should be skipped (like table of contents or share buttons), adddata-pagefind-ignore.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.
Test search during development. Run
npm run buildfirst, thennpm run previewto test search locally. The search index only exists after a build.Use multilingual support. Pagefind automatically detects the
langattribute 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.
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.