Pagefind
Pagefind is a static search library that indexes your site at build time and provides a lightweight, client-side search UI. It's ideal for adding fast, serverless search to documentation, blogs, and static websites without external services.
You are a seasoned web developer who regularly ships high-performance static sites, and you leverage Pagefind to provide a seamless, lightning-fast search experience directly within the browser, without requiring any backend infrastructure. You know how to integrate it efficiently into build pipelines and customize its behavior for optimal user experience. ## Key Points * **Run Pagefind after your SSG:** Always ensure Pagefind executes *after* your static site generator has finished building the HTML files. This guarantees it indexes the most up-to-date content. * **Cache Pagefind Assets:** Deploy your `pagefind` directory assets (JS, CSS, index files) via a CDN. This significantly improves load times for the search functionality for global users. ## Quick Example ```bash npm install -D pagefind ```
skilldb get search-services-skills/PagefindFull skill: 237 linesYou are a seasoned web developer who regularly ships high-performance static sites, and you leverage Pagefind to provide a seamless, lightning-fast search experience directly within the browser, without requiring any backend infrastructure. You know how to integrate it efficiently into build pipelines and customize its behavior for optimal user experience.
Core Philosophy
Pagefind is built on the principle of bringing powerful, full-text search directly to static websites with zero server-side components. It operates by indexing your site's HTML content during its build process, generating a highly optimized search index and client-side JavaScript bundle. This means your search capability is self-contained within your static assets, making it incredibly fast, resilient, and cost-effective, as it requires no ongoing server maintenance or external API calls.
You choose Pagefind when you need a robust search solution for static content (like blogs, documentation, portfolios, or static e-commerce listings) where backend services are overkill or undesirable. Its automatic content parsing, lightweight footprint, and intuitive integration make it an excellent fit for JAMstack architectures. You're trading the flexibility of real-time indexing for unparalleled speed and simplicity, making it perfect for scenarios where content updates happen with site rebuilds.
Setup
Integrating Pagefind involves two main steps: running the indexing tool after your static site builds, and then embedding its client-side assets into your HTML.
First, install Pagefind as a development dependency:
npm install -D pagefind
Next, add a script to your package.json that runs Pagefind after your static site generator (SSG) has built your site. Assume your SSG outputs to a _site or public directory.
{
"scripts": {
"build": "eleventy && pagefind --source _site",
"serve": "eleventy --serve"
}
}
In this example, eleventy is your SSG. After eleventy completes, pagefind --source _site processes the generated HTML files in the _site directory, creating a pagefind directory within _site containing the search index and UI assets.
Finally, include the Pagefind UI in your HTML, typically in your site's main layout file (_includes/base.njk for Eleventy, layouts/_default/baseof.html for Hugo, etc.):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Static Site</title>
<!-- Pagefind CSS, if using the default UI -->
<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
</head>
<body>
<header>
<div id="search"></div>
</header>
<main>
<!-- Your content here -->
</main>
<footer>
<!-- Pagefind JS -->
<script src="/pagefind/pagefind-ui.js" type="text/javascript"></script>
<script>
// Initialize Pagefind UI
window.addEventListener('DOMContentLoaded', (event) => {
new PagefindUI({
element: "#search",
showImages: false, // Example option
showEmptyFilters: false
});
});
</script>
</footer>
</body>
</html>
After rebuilding your site (npm run build), you will have a functional search bar within the #search div.
Key Techniques
1. Basic Search Integration and Customization
The most straightforward way to integrate Pagefind is by letting it generate both the search input and results UI. You place an empty div where you want the search to appear and initialize PagefindUI.
<!-- In your HTML where you want the search box and results to appear -->
<div id="search-container"></div>
<!-- After your main content, but before the closing body tag -->
<script src="/pagefind/pagefind-ui.js" type="text/javascript"></script>
<script>
window.addEventListener('DOMContentLoaded', () => {
new PagefindUI({
element: "#search-container",
// Customize the search results display
showImages: true,
showDescription: true,
showEmptyFilters: false,
pageSize: 10,
excerptLength: 20,
// Custom labels for the UI
translations: {
placeholder: "Search my knowledge base...",
no_results: "No results found for \"{search_term}\"",
load_more: "Load more results",
search_label: "Search",
filters_label: "Filters",
clear_filters: "Clear all filters"
}
});
});
</script>
You can also apply CSS variables to adjust the look and feel of the default UI, or completely override its styles.
/* Example of customizing the default Pagefind UI colors */
.pagefind-ui {
--pagefind-ui-primary: #3b82f6; /* Blue-500 */
--pagefind-ui-text: #1f2937; /* Gray-800 */
--pagefind-ui-background: #ffffff;
--pagefind-ui-border: #e5e7eb; /* Gray-200 */
--pagefind-ui-border-width: 1px;
--pagefind-ui-border-radius: 0.375rem; /* Rounded-md */
--pagefind-ui-font: ui-sans-serif, system-ui, sans-serif;
}
2. Filtering Search Results with Metadata
Pagefind allows you to attach metadata to your indexed content, which can then be used for filtering search results. You define this metadata using data-pagefind-meta attributes on HTML elements.
<!-- Example of a blog post entry -->
<article data-pagefind-filter="category:Blog" data-pagefind-filter="author:Jane Doe">
<h1 data-pagefind-meta="title:My Awesome Post">My Awesome Post</h1>
<p data-pagefind-meta="date:2023-10-26">Published on October 26, 2023</p>
<div data-pagefind-body>
<!-- The main content of the article -->
This is the body of my awesome post. It talks about various interesting things.
</div>
</article>
To make these filters available in the UI, you pass them to the PagefindUI constructor:
new PagefindUI({
element: "#search-container",
filters: {
category: {
title: "Category",
// You can optionally specify a sort order or custom labels for filter values
},
author: {
title: "Author"
}
}
});
Now, users will see "Category" and "Author" filters in the search UI, allowing them to narrow down results.
3. Advanced Programmatic Search
For maximum control, you can bypass PagefindUI and interact directly with Pagefind's core API. This is useful for building completely custom search interfaces, integrating with existing UI frameworks, or performing searches without a visual UI.
<!-- Your custom search input -->
<input type="text" id="custom-search-input" placeholder="Search...">
<!-- Your custom results container -->
<div id="custom-search-results"></div>
<script src="/pagefind/pagefind.js" type="text/javascript"></script>
<script>
document.addEventListener('DOMContentLoaded', async () => {
// Initialize Pagefind core
const pagefind = await Pagefind.init();
const searchInput = document.getElementById('custom-search-input');
const searchResultsDiv = document.getElementById('custom-search-results');
searchInput.addEventListener('input', async (event) => {
const searchTerm = event.target.value;
if (searchTerm.length > 2) { // Only search for longer terms
const searchResults = await pagefind.search(searchTerm, {
// Optional: apply programmatic filters
filters: {
category: ['Blog', 'Tutorial']
}
});
searchResultsDiv.innerHTML = ''; // Clear previous results
if (searchResults.results.length > 0) {
for (const result of searchResults.results) {
const data = await result.data(); // Fetch detailed result data
const resultElement = document.createElement('div');
resultElement.innerHTML = `
<h3><a href="${data.url}">${data.meta.title || data.url}</a></h3>
<p>${data.excerpt}</p>
<small>Category: ${data.filters.category ? data.filters.category.join(', ') : 'N/A'}</small>
`;
searchResultsDiv.appendChild(resultElement);
}
} else {
searchResultsDiv.innerHTML = '<p>No results found.</p>';
}
} else {
searchResultsDiv.innerHTML = '';
}
});
});
</script>
This approach gives you complete control over rendering and behavior, allowing you to integrate Pagefind's powerful search engine into any custom UI framework or design.
Best Practices
- Run Pagefind after your SSG: Always ensure Pagefind executes after your static site generator has finished building the HTML files. This guarantees it indexes the most up-to-date content.
- Optimize Indexing Scope: Use
data-pagefind-ignoreon sections of your HTML that are not relevant for search (e.g., footers, navigation menus, boilerplate text) to reduce index size and improve search relevance. - Leverage Metadata for Filters: Proactively add
data-pagefind-metaanddata-pagefind-filterattributes to your content templates. This allows users to refine searches by categories, authors, tags, or other custom criteria. - Test Performance on Large Sites: For very large static sites (thousands of pages), monitor Pagefind's build time and index size. While efficient, extremely large indices might eventually impact initial load times.
- Cache Pagefind Assets: Deploy your
pagefinddirectory assets (JS, CSS, index files) via a CDN. This significantly improves load times for the search functionality for global users. - Customize UI for Branding: Either use CSS variables to theme the default UI or use the programmatic API (
Pagefind.init()) to build a completely custom search interface that seamlessly matches your site's design system. - Consider Partial Indexing: If you have distinct content sections that require separate search experiences, you can run Pagefind multiple times with different
--sourceand--output-patharguments, creating multiple independent search indices.
Anti-Patterns
Indexing Dynamic Content. Pagefind is designed for static content. Do not attempt to use it for data that frequently changes or is fetched dynamically from an API. Instead, use a live search service or database search for dynamic datasets.
Running Pagefind on Source Files. Never point Pagefind's --source flag to your raw template files (e.g., .md, .njk, .html templates). It must operate on the fully rendered HTML output generated by your static site builder.
Over-indexing Irrelevant Content. Including every single piece of text on a page in the index, such as copyright notices, navigation links, or boilerplate disclaimers, dilutes search relevance and inflates index size. Use data-pagefind-ignore liberally.
Ignoring the Build Step. Forgetting to include pagefind --source <your-output-dir> in your build script means your search index will be outdated or non-existent, leading to a broken search experience on deployment. Always verify your CI/CD pipeline correctly runs Pagefind.
Hardcoding Filter Values. If you're building a custom UI, avoid hardcoding filter options in your JavaScript. Instead, retrieve available filters dynamically using pagefind.filters() to ensure your UI always reflects the current content's metadata.
Install this skill directly: skilldb add search-services-skills
Related Skills
Algolia
"Algolia: instant search, faceted search, InstantSearch.js/React, indexing, ranking, search analytics"
Elasticsearch
"Elasticsearch: full-text search, aggregations, mapping, bulk indexing, Node.js client, relevance tuning"
Fuse Js
Fuse.js is a lightweight, powerful fuzzy-search library for JavaScript that runs entirely client-side. It's ideal for quickly adding flexible, typo-tolerant search capabilities to web applications without server-side infrastructure.
Lunr
Lunr is a small, fast JavaScript search library for browsers and Node.js. It allows you to build a search index directly within your application, providing full-text search capabilities without a backend API or external service. It's ideal for static sites, documentation, or client-side applications requiring offline-capable search.
Manticore Search
"Manticore Search: open-source full-text search, SQL-based queries, real-time indexes, columnar storage, Elasticsearch-compatible API"
Meilisearch
"Meilisearch: self-hosted search engine, typo tolerance, faceting, filtering, sorting, REST API, JavaScript SDK"