Implementing full-text search with Astro and Pagefind.
Ever opened a website and wanted to search for something, only to find that there’s no search functionality?
It sucks! Specifically if you have to sift through a ton of content to find a specific thing. This happens quite often when you’re trying to find a specific piece of information on a documentation site.
A common reason why a lot of these sites don’t have a search function is that implementing search is painful. It involves setting up a search engine, crawling the site, indexing the content, designing a search interface, and then repeating the entire process every time you update the site.
It’s a lot of work and requires maintaining a search engine just for your static website. So, it’s not surprising that a lot of folks don’t bother.
What if there was a better way?
Earlier today, I was thinking of building an in-memory search index over my static outputs and serving it as an endpoint, so that I could avoid running an entirely separate search engine.
There are a decent number of packages that implement search indices and are more than enough for a small website with a few dozen or a hundred pages.
But before jumping into that, I started looking for a simpler solution, and I stumbled upon Pagefind.
Pagefind takes an even simpler approach to my half-baked idea. It generates static search indices from your build output and serves them as static files that you can serve right alongside your website.
It doesn’t require any server-side code, moves the search generation to the build step, and does the search on the client side. It automatically updates the search index when you update your site, and it’s super easy to set up.
It’s seriously the best of both worlds.
The nice thing about Pagefind is that it’s a standalone service that you can use with any static site generator. It doesn’t require any specific setup or configuration, and it’s super easy to use.
Implementing Pagefind in Astro
First, we need to install the Pagefind CLI. This is the tool that generates the search index and all the other files that we need.
npm install pagefind --save-dev
Next, we need to configure it to generate the search index for our site.
{
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro check && astro build",
"postbuild": "pagefind --site dist",
"preview": "astro preview",
"astro": "astro"
}
}
Here, I have added a post-build script that runs Pagefind after we are done building the site. This will generate all the necessary files in the dist
directory, which will be served alongside our site.
Adding the search bar
Now that we have the search index generated, we need to add a search bar to our site. Thankfully, Pagefind provides a simple search bar that we can use and makes it super easy to customize it to match our site’s design.
All we need to do is add the following script where we want the search bar to appear.
<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
<script is:inline src="/pagefind/pagefind-ui.js"></script>
<div id="search"></div>
<script>
window.addEventListener('DOMContentLoaded', (event) => {
new PagefindUI({ element: '#search', showSubResults: true });
});
</script>
And that’s it! We now have a search bar on our site that will search through all the content on our site.
However, there’s one little gotcha that we need to fix. By default, Pagefind will spit out the search index in the root of the dist
directory. The problem with this is that the scripts and the index files will not be accessible during development.
To fix this, I have added a pre-dev script that runs the Pagefind CLI before starting the development server, and instead, copies the output to the public
directory.
{
"scripts": {
"dev": "astro dev",
"predev": "pagefind --site dist --output-path ./public/pagefind",
"start": "astro dev",
"build": "astro check && astro build",
"postbuild": "pagefind --site dist",
"preview": "astro preview",
"astro": "astro"
}
}
The only problem with this approach is that the dist
directory will need to exist before running the dev server, but that shouldn’t be a big deal.
And that’s it! We now have a full-text search functionality on our Astro site with only a few lines of code.
Parting thoughts
Pagefind provides a ton of customization options that you can use to tweak the search bar, the search results, indexing options, and a lot more.
For example, you may want different weights for different parts of the content:
<body>
<p data-pagefind-weight="2">
The main description text of the page. If the search term matches this
section, this page will be boosted higher in the result ranking.
</p>
<p>Other, less important text. This defaults to a weight of 1.</p>
<p data-pagefind-weight="0.5">
Very unimportant text. Matching words in this block are only worth half a
normal word.
</p>
</body>
Or you may want to exclude certain parts of the content from the search index:
<body>
<p data-pagefind-ignore>
This text will not be indexed by Pagefind and will not show up in search
results.
</p>
</body>
There are a ton of other options that you can find on their website.