Yesterday I was contemplating the idea of [adding a search bar to this blog]({{< relref "is-it-the-time-to-add-a-search-functionality" >}}). 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
.
-
Which is absolutely not POSIX. 😁↩