Search with Pagefind

Integrate Pagefind with Hugo to perform client side full text search
Sat Aug 17, 2024
644 Words

I’m currently using Pagefind to implement client side search for this site. It only took me a couple of hours to get it running and added to my deployment pipeline. If you’re looking for a simple and free search solution for your hugo site I highly recommend Pagefind!

I followed their quickstart documentation which was pretty straight forward. This page will document all the work I needed to do to get it working for my setup.

Integration

Pagefind generates page indexes using the static files that Hugo exports when you run hugo. You can run the following command to generate the page indexes.

1
npx pagefind --site public

This will produce a new pagefind/ directory in your public/ directory. Within this folder will be some pre-generated javascript and css files you can import to add a search component. This search component does all the heavy lifting (e.g. input handling, displaying search results, etc.) so once you embed it you can beginning searching the contents of your site.

Within the content/ directory I added a new directory called search/ with the following index.html.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<script src="/pagefind/pagefind-ui.js"></script>
<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />

<div id="search"></div>

<script>
  window.addEventListener("DOMContentLoaded", (event) => {
    new PagefindUI({
      element: "#search",
      showSubResults: true,
      showImages: false,
      autofocus: true,
    });
  });
</script>

Now when we navigate to localhost:1313/search/ we can see the component and start searching!

Styling

The default css Pagefind produces formats the search component and results nicely however the colors didn’t integrate well with my setup. Luckily, Pagefind exposes some CSS variables that we can use to override the style.

This site supports both light and dark mode so I had to define a custom style for each mode. Dark mode is controlled by toggling the dark class in the html so the css is configured accordingly.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
html:not(.dark) {
  --pagefind-ui-background: #fffbeb;
  --pagefind-ui-text: #1f2937;

  --pagefind-ui-border: #4b5563;
  --pagefind-ui-tag: #eeeeee;

  --pagefind-ui-font: "JetBrains Mono", monospace;
  --pagefind-ui-border-width: 2px;
  --pagefind-ui-border-radius: 0px;
}

html.dark {
  --pagefind-ui-background: #52525b;
  --pagefind-ui-text: #e4e4e7;

  --pagefind-ui-border: #52525b;
  --pagefind-ui-tag: #eeeeee;

  --pagefind-ui-font: "JetBrains Mono", monospace;
  --pagefind-ui-border-width: 2px;
  --pagefind-ui-border-radius: 0px;
}

Restrict Indexing to Posts

By default, Pagefind will index the content in the <body> tag in all html files under your public directory. In my case I wanted it to only index the content under the posts directory. To restrict the indexing I added a data-pagefind-body tag to the <main> block in my layouts/posts/single.html file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<main data-pagefind-body>
  <div class="p-4">
    <div class="flex flex-col gap-1">
      <h1 class="text-2xl m-0 font-bold">{{.Title}}</h1>
      <div class="text-sm dark:text-zinc-500">{{ .Description }}</div>
      <div class="text-sm dark:text-zinc-500">
        {{.Date.Format "Mon Jan 2, 2006"}}
      </div>
      <div class="text-sm dark:text-zinc-500">{{.WordCount}} Words</div>
      {{ partial "tags.html" .}}
    </div>
    <article class="pt-4" id="content">{{ .Content }}</article>
  </div>
</main>

With this tag included we need to rebuild the site and rerun the indexing command and the search component should now only yield us results from our posts directory.

Github Actions

Finally, to make sure Pagefind is built and deployed alongside our site we’ll need to update our deployment workflow. In your workflow, after you run your build command simply add the following command and you’re good to go.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
- name: Build with Hugo
  env:
    # For maximum backward compatibility with Hugo modules
    HUGO_ENVIRONMENT: production
    HUGO_ENV: production
  run: |
    hugo \
      --minify \
      --baseURL "${{ steps.pages.outputs.base_url }}/"
- name: Build pagefind index
  run: |
    npx pagefind --site public

Conclusion

After running a couple of commands and editting a couple of files we have search support for our site! Pagefind is a great library for those looking for a simple and free search solution to integrate in their next Hugo project.