Integrating Pagefind Full-Text Search Engine into a Next.js SSG Blog
What is Pagefind?
Pagefind is a recently developed full-text search engine that runs on the web. The official description states:
Pagefind is a fully static search library that aims to perform well on large sites, while using as little of your users' bandwidth as possible, and without hosting any infrastructure.Pagefind runs after Hugo, Eleventy, Jekyll, Next, Astro, SvelteKit, or any other website framework. The installation process is always the same: Pagefind only requires a folder containing the built static files of your website, so in most cases no configuration is needed to get started.
As you can see from , it is written in (WebAssembly). The stemming -> indexing -> search pipeline appears to be WebAssembly-based.
Japanese Language Support
Multilingual support is available. Japanese support is limited, but apparently Chinese stemming is applied instead of whitespace-based stemming. The supported language is determined by referencing the HTML lang attribute.
Also, segmentation during search is not supported, so tokenization of compound words doesn't happen at search time. You need to do whitespace splitting yourself. Stemming details are written here, but I'm not familiar enough to fully understand the details. It doesn't seem to use dictionaries and appears to be rule-based.
Currently when indexing, Pagefind does not support stemming for specialized languages, but does support segmentation for words not separated by whitespace.s Pagefind does not yet support segmentation of the search query, so searching in the browser requires that words in the search query are separated by whitespace. In practice, this means that on a page tagged as a zh- language, 每個月都 will be indexed as the words 每個, 月, and 都. When searching in the browser, searching for 每個, 月, or 都 individually will work. Additionally, searching 每個 月 都 will return results containing each word in any order, and searching "每個 月 都" in quotes will match 每個月都 exactly. Searching for 每個月都 will return zero results, as Pagefind is not able to segment it into words in the browser. Work to improve this is underway and will hopefully remove this limitation in the future.
Search Algorithm
I couldn't find information about the search algorithm on the official page, but reading the code, it appears to use BM25 as of (2024/09/23).
Implementation in Next.js
This assumes SSG. I'll skip the Next.js configuration details, but output: "export" is required.
Setup
npm-run-all is used as a task runner.
In the pagefind CLI, specify the out directory generated by next build under --site.
This generates the index, pagefind.js, etc. under out/pagefind/ (unless --output-path is specified).
For reference, roughly the following is output:
Details
Also, pagefind.js needs to be loadable in the dev environment as well, so during development, we generate it under public/pagefind.
Since we don't want this in git, add it to .gitignore.
.gitignore
Writing the Component
- Use
useEffectto dynamically import/pagefind/pagefind.js.- This path should match the output path specified in the
pagefindCLI.
- This path should match the output path specified in the
- Zod is used for type-safe result retrieval.
For type information, refer to the following. I only used the parts that seemed necessary.
Specifying Where to Index
You can control where indexing occurs by specifying data-pagefind-* tags.
The most straightforward example is specifying data-pagefind-body, which causes only the content within main to be indexed:
You can also exclude specific sections from indexing by specifying data-pagefind-ignore.
For more details, see:
Conclusion
I was impressed by how easy it was to add a search engine to an SSG site. This makes it seem like you can host a blog on Cloudflare or similar services with all the necessary features.