Astro Local Search Without External Services (Pagefind)
Self-hosted search index at build time. No Algolia bill, no API keys, works on static hosting.
Tested on: Astro 5.7, Pagefind 1.3 Indexed: 47 pages. Search index size: 180 KB. Monthly cost: $0.
Install
npx astro add pagefind
# or manually:
npm install -D pagefind
astro.config.mjs
import { defineConfig } from 'astro/config';
import pagefind from 'astro-pagefind';
export default defineConfig({
site: 'https://myblog.dev',
integrations: [pagefind()]
});
Build generates /pagefind/ directory with WASM search index.
Mark searchable content
Wrap main content in any layout:
<main data-pagefind-body>
<slot />
</main>
Exclude nav/footer:
<nav data-pagefind-ignore> ... </nav>
Search UI component
src/components/Search.astro:
<div id="search"></div>
<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
<script src="/pagefind/pagefind-ui.js"></script>
<script>
window.addEventListener('DOMContentLoaded', () => {
new PagefindUI({ element: '#search', showSubResults: true });
});
</script>
Or use a Svelte/React island with client:idle for fancier UI.
Build error — site URL missing
Pagefind: Cannot resolve relative URLs without astro.site configured
astro.config.mjs:
export default defineConfig({
site: 'https://myblog.dev' // required, even for local builds
});
Production 404 on /pagefind/
Cause: hosting didn't upload pagefind/ folder.
Verify:
npm run build
ls dist/pagefind/
# pagefind.js, pagefind-ui.js, index/*.pf_segment
Netlify/Cloudflare: no special config needed if output is dist/.
Exclude draft posts
In content collection schema, add draft: boolean. Filter in getStaticPaths. Pagefind only indexes built HTML — drafts never in dist/, never indexed.
Performance
Initial page load: unchanged (search UI lazy on /search page)
/pagefind/pagefind.wasm: 63 KB
Full index: 180 KB (downloaded on first search)
Search latency: <50ms locally
Algolia free tier caps at 10k records. Pagefind doesn't care.