Adding a search bar for Hugo with Pagefind

Yesterday I was contemplating the idea of adding a search bar to this blog. Alright, being honest, this is a tough decision. I really like the idea of my website weighting almost nothing, but I also believe that I should make it easier for people to search for what they are looking after, including myself.

I’ll write this as a fun exercise and documentation for the future me. At the end of it, I might have a search bar or not. 🤪

After some research, I decided to settle with Pagefind due to it’s “zero-config” nature. I really like almost zero-config software! Now, let the fun begin.

Filtering content and adding metadata

Skimming through the documentation, I found what is needed to only show my posts on “Limiting what sections of a page are indexed”. This means that I only need to put the attribute data-pagefind-body on my layouts/_default/single.html layout to have it ignore tags and other Hugo Taxonomies.

It’s also possible to add metadata do the pages can also add metadata with the data-pagefind-meta attribute:

<!-- layouts/_default/single.html -->

<!-- ... -->

{{ define "main" }}
  <article data-pagefind-body>
    <h1 data-pagefind-meta="title">{{ .Title }}</h1>
    <!-- ... -->
    <time
      data-pagefind-meta="date[datetime]"
      datetime="{{ .Date.Format "2006-01-02" }}" pubdate>
        {{ .Date.Format "02 Jan 2006" }}
    </time>
    <!-- ... -->
  </article>
{{ end }}

One caveat that I found was that using the attribute data-pagefind-meta="title" would make the output look weird with a bunch of ampersands. This problem is being tracked on the GitHub Issue #459.

Adding the Pagefind UI component

Fortunately, Pagefind does provide a default UI component that is rather easy to use. You can find the JavaScript file for it on the output of the indexing command:

$ pagefind --site public --output-path=static/js/pagefind
# the file will at static/js/pagefind/pagefind-ui.js

One tip I can give is to gitignore the Pagefind output directory, you don’t need to commit it to your repository as you can always regenerate it. Now, at the end of my layouts/_default/list.html file I add the needed code to show the search bar.

<!-- layouts/_default/list.html -->

<!-- ... -->

<script src="/js/pagefind/pagefind-ui.js" type="text/javascript"></script>
<script>
  window.addEventListener('DOMContentLoaded', (event) => {
    new PagefindUI({
      baseUrl: "/",
      // search element id
      element: "#search",
      // do not show images
      showImages: false,
      // I want to use my own CSS
      resetStyles: true,
      // do not show subresults of the same page
      showSubResults: false,
    });
  });
</script>
<div id="search"></div>

Building your website

This whole setup does introduce some build complexities to the whole website. This is mostly caused by all the tools not having a common interface to talk to. I’m glad this whole website is orchestrated with a single Makefile1. Let’s try to solve this dependency puzzle with the help of our beloved GNU Make.

The first thing we need to build our index, is the public directory Hugo generates. After this, we can run the Pagefind CLI and then publish our website.

public:
	hugo
	pagefind --site public --output-path=static/js/pagefind
	# copies pagefind index to the public directory
	cp -r static/js public

publish: public
	echo "your publishing command"

run: public
	hugo server --buildDrafts --buildFuture

clean:
	rm -rf public
	rm -rf site.tar.gz
	rm -rf static/js/pagefind

.PHONY: clean run publish

This is a nice trick to use, run depending on public ensures that we also have the search bar on our development enviroment with hugo server.


  1. Which is absolutely not POSIX. 😁 ↩︎


Articles from blogs I follow around the net

Trimming a Fake Object

A refactoring example. When I introduce the Fake Object testing pattern to people, a common concern is the maintenance burden of it. The point of the pattern is that you write some 'working' code only for test purposes. …

via ploeh blog November 20, 2023

Building a digital vigil for those we've lost

This post is hard to write in a lot of ways. It's more personal than most I've written. This is presumptively a tech blog, and this piece is about so much more than technology. But it's important. Making things, software or otherwise, is ultimatel…

via ntietz.com blog November 19, 2023

#122 Experimenting and Learning

Update on what happened across the GNOME project in the week from November 10 to November 17. GNOME Circle Apps and Libraries Workbench A sandbox to learn and prototype with GNOME technologies. Sonny says Workbench is a code playground and Library to learn, e…

via This Week in GNOME November 17, 2023

Generated by openring