Add search to your Next.js static site with Pagefind
- Planted:
Pagefind is a neat OSS client-side search library that just recently reached v1.0.0
in September ’23. From the docs:
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.
The docs do a terrific job of explaining things, and the search experience feels really snappy (hit ⌘+K
to give it a try). To get Pagefind working with Next.js, it takes a few extra steps.
Run pagefind after build
Start by installing Pagefind as a dev dependency:
npm i -D pagefind
The Pagefind CLI generates a static search index from your site’s static files after building. The --site
flag indicates the output dir of your built static files, and the --output-path
option is where the search bundle will be written—more on that below. I’m on Next 13.4.9
and the /pages
router, so adjust as needed.
I’m using pnpm, which doesn’t support pre-
and post-
scripts by default, so I just appended && <pagefind-script>
to my build script. You can also enable pre- and post- scripts with pnpm.
Depending on how you build and deploy, you might be able to avoid adding the dependency and postbuild script altogether by running npx pagefind
instead.
Dynamically import search bundle
The tricky bits that tripped me up were (1) handling errors in development, and (2) configuring Webpack to allow the import to resolve correctly.
The first one is an issue because the Pagefind search index is generated after build, so it doesn’t exist when running next dev
. One way to solve this is creating a /pagefind/pagefind.js
file that returns some mock search results in development, or you can add a try-catch and @ts-expect-error
comment to avoid creating the extra file (thanks to noxify for the tip!).
The second problem is that Webpack will bundle /pagefind/pagefind.js
and output the contents into a new file, so the import path will hit a dead end. That’s where the webpackIgnore
comment comes in handy, preserving the initial path. Files in the /pages
directory (in v13.4.9
, at least) will end up in the .next/static/chunks/pages
folder upon build, hence the custom --output-path
discussed above.
The search implementation on this website is public, so feel free to poke around (and suggest anything I could improve upon!).
Bells ’n whistles
Pagefind exposes some handy options for refining your index and polishing search UI. I am using both the data-pagefind-body
and data-pagefind-ignore
attributes to exclude certain content from my search index (e.g. header, 404 page, homepage, etc.). I also swapped pagefind.debouncedSearch()
in place of the standard pagefind.search()
to cut down on resource usage and prevent jarring flashes of results before you finish typing a search query.
There are options to filter and sort results, too, which I may tap into down the road. Check out the docs for other neat options, like multilingual support.
Shoutouts
Thanks to Otterlord’s blog post on using Pagefind with Astro for the nudge to share something similar for Next. If you’re keen to use Pagefind with another SSG, the docs have a list of community resources. There are also some nice demos to get a feel for the search experience before jumping into your own implementation.
And big, big thanks to bglw for helping me debug this and creating an amazing library!
Backlinks
- How does search work, exactly?
- #1 — January 2024
- My next, next, next job
- Building an intuition for file size and network speed
- Interaction to Next Paint (INP)