Table of Contents
Overview
In the previous post Ad Integration (Google AdSense), we covered ad integration. In this post, I’ll explain how to implement search functionality on a static site without a server.
What is Pagefind
Pagefind is a search library for static sites. It analyzes built HTML files to generate a search index and performs searches on the client side.
Key Features
Unlike other search solutions (Algolia, Elasticsearch, etc.), the biggest advantage of Pagefind is that it works with just a static site — no separate server required.
- No Server Required: Generates indexes at build time and serves them as static files
- Lightweight: Small search index and light JavaScript bundle
- Fast Search: Runs quickly in the browser using WebAssembly
- Multilingual Support: Language-specific indexing available
Installation
Since Pagefind is only used during build, install it as a dev dependency (-D).
npm install -D pagefind
Build Integration
The Pagefind indexing runs after the Astro build in the build script of package.json.
{
"scripts": {
"build": "astro build && npx pagefind --site dist"
}
}
Pagefind analyzes HTML files in the dist/ directory and generates search indexes in the dist/pagefind/ directory. Therefore, it must be run after astro build.
Specifying Search Targets
By default, Pagefind indexes all text on a page. However, if non-body areas like navigation and footer are also searched, unnecessary results may be exposed. Using the data-pagefind-body attribute, you can limit the search target to the body area only.
<!-- src/layouts/PostLayout.astro -->
<article data-pagefind-body>
<slot />
<!-- Markdown content -->
</article>
Use the data-pagefind-ignore attribute on elements to exclude from search.
Metadata Filters
To filter search results by category, specify metadata with the data-pagefind-filter attribute. The text of the element containing this attribute is used as the filter value.
<!-- src/layouts/PostLayout.astro -->
<span data-pagefind-filter="category">{category}</span>
Search Page Implementation
Search functionality is implemented with SearchBar.astro (homepage search bar) and SearchPage.astro (dedicated search page).
SearchPage.astro Main Structure
The search page consists of category filter chips, search status display, search results list, and pagination. Category lists and translation data are prepared on the server side, while actual searching is handled by client-side JavaScript.
---
// src/components/SearchPage.astro
import { useTranslations, getLocalizedPath } from '../i18n/translations';
import { getMainCategories } from '../data/categories';
const { lang } = Astro.props;
const t = useTranslations(lang);
const mainCategories = getMainCategories();
---
<div class="search-page">
<!-- Category filter chips -->
<div class="search-page__filters" id="search-filters">
<button class="search-page__chip active" data-category="">
{t('search.allCategories')}
</button>
{categoryChips.map((chip) => (
<button class="search-page__chip" data-category={chip.slug}>
{chip.title}
</button>
))}
</div>
<!-- Search status -->
<div class="search-page__status" id="search-status"></div>
<!-- Search results -->
<div class="search-page__results" id="search-results"></div>
<!-- Pagination -->
<nav id="search-pagination" class="search-page__pagination"></nav>
</div>
Client-Side Search Logic
The actual search runs in the browser. Using Pagefind’s JavaScript API, when a search query is entered, it finds and returns results from the index generated at build time.
// Client script in src/components/SearchPage.astro
const pagefind = await import('/pagefind/pagefind.js');
await pagefind.init();
// Execute search
const search = await pagefind.search(query);
// Load results
const results = await Promise.all(search.results.map((r) => r.data()));
Since URL query parameters (?q=search term) are used to persist the search query, the search results page can be bookmarked or shared.
Multilingual Search
This blog supports Japanese, Korean, and English, so separate search pages are provided for each language.
| Language | Search Page URL |
|---|---|
| Japanese | /search/ |
| Korean | /ko/search/ |
| English | /en/search/ |
Pagefind’s index includes content from all languages, but only results for the current language are filtered and displayed during search.
Category Filtering
When there are many search results, it’s convenient to be able to pick only the categories you want. Category lists defined in categories.ts are displayed as chip buttons, and results are filtered by the category the user selects.
// Client script in src/components/SearchPage.astro
const search = await pagefind.search(query, {
filters: {
category: selectedCategory,
},
});
Comparison with Jekyll Search
This blog was originally running on Jekyll and is being migrated to Astro. For the Jekyll search implementation, refer to the Creating a Search Bar post.
Here are the key differences between Jekyll and Astro (Pagefind) search implementations:
| Item | Jekyll | Astro (Pagefind) |
|---|---|---|
| Search method | JSON file-based client search | WebAssembly-based index search |
| Index size | Full content generated as JSON (large) | Optimized binary index (small) |
| Search speed | JavaScript string matching | Fast search with WebAssembly |
| Setup complexity | Custom JSON generation + search logic needed | Index generation with one npx pagefind command |
| Category filter | Manual implementation needed | Simple implementation with data-pagefind-filter |
Conclusion
In this post, we looked at implementing static search with Pagefind.
- Implementing client-side search without a server using
Pagefind - Integrating into the build pipeline with
astro build && npx pagefind --site dist - Specifying search targets and filters with
data-pagefind-bodyanddata-pagefind-filter - Supporting multilingual search with language-specific search pages
- Filtering results with category chip buttons
In the next post Layout and Component Architecture, we’ll cover the blog’s layout and component structure.
Series Guide
This post is part of the Jekyll to Astro migration series.
- Why I Migrated from Jekyll to Astro
- Astro Installation and Project Setup
- Content Collections and Markdown Migration
- Multilingual (i18n) Implementation
- SEO Implementation
- Image Optimization — Custom rehype Plugin
- Comment System (Utterances)
- Ad Integration (Google AdSense)
- Search Implementation with Pagefind
- Layout and Component Architecture
- GitHub Pages Deployment
- Social Share Automation Script
- Troubleshooting and Tips
Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!
App promotion
Deku.Deku created the applications with Flutter.If you have interested, please try to download them for free.