Hugo Website Search with Pagefind
I integrated Pagefind into my website for a website text content search.
Pagefind is quite interesting in that it builds a search index as part of the build process.
- Generate static website files with Hugo
- Call Pagefind; specify CSS selector inclusions and exclusions to indicate the main content that searching should discover
- Pagefind generates a sectioned search index
- The website loads the Pagefind JavaScript library and provides some display rendering
- When and while the user types into the search input, Pagefind loads the appropriate search index sections on demand
This means that Pagefind is
- very cleanly separated from the website logic - with some integration/analysis overhead but no system-integration complexity
- very efficient search index; no live text search; sectioned index and loading means minimal bandwidth and latency
Pagefind provides a default UI for simple integration, but that felt too heavy and not fitting to me. I implemented my own simple HTML rendering and CSS styling.
I’d like to add better keyboard UX for it, but tabbing works for now. The input could also look a bit better.
I’m glad it’s finally there though, and works well with snappy search and result listing.
GitHub CI Integration of Pagefind
My GitHub CI step calls pagefind --site "public" --root-selector "main#main" --exclude-selectors "aside" --verbose
after building into public
with a simple hugo
call.
The pagefind executable is made available through a cached dependency install into the Ubuntu runner.
With PAGEFIND_VERSION
and PAGEFIND_CHECKSUM
defined as environment variables, Pagefind is downloaded through wget --no-verbose https://github.com/CloudCannon/pagefind/releases/download/v${{ env.PAGEFIND_VERSION }}/pagefind-v${{ env.PAGEFIND_VERSION }}-x86_64-unknown-linux-musl.tar.gz -O pagefind.tar.gz
and then checked with echo "${{ env.PAGEFIND_CHECKSUM }} pagefind.tar.gz" | sha256sum --strict --check --status
. The release checksum can be found in the release pagefind-v<VER>-x86_64-unknown-linux-musl.tar.gz.sha256
file.
The executable is then moved into /usr/local/bin
through tar -xf pagefind.tar.gz
, rm pagefind.tar.gz
, mv pagefind /usr/local/bin/pagefind
.
jobs:
full:
runs-on: ubuntu-latest
env:
HUGO_VERSION: '0.139.3'
HUGO_CHECKSUM: '3e58800d1fee57269208d07d104ae1a6ab886615344099f2dca0c6ad5279bc11'
PAGEFIND_VERSION: '1.3.0'
PAGEFIND_CHECKSUM: '5dfb56609c2d08058c3be56a1a2d332d8dc50d9a6c74f20b3619eadb53240af3'
steps:
- uses: actions/checkout@v4
- id: cache-hugo
uses: actions/cache@v4
with:
path: /usr/local/bin/hugo
key: ${{ runner.os }}-hugo-${{ env.HUGO_VERSION }}
- if: success() && steps.cache-hugo.outputs.cache-hit != 'true'
name: Get Hugo
run: |
set -e
wget --no-verbose https://github.com/gohugoio/hugo/releases/download/v${{ env.HUGO_VERSION }}/hugo_extended_${{ env.HUGO_VERSION }}_linux-amd64.deb -O hugo.deb
echo "${{ env.HUGO_CHECKSUM }} hugo.deb" | sha256sum --strict --check --status
sudo dpkg -i hugo.deb
rm hugo.deb
- id: cache-pagefind
uses: actions/cache@v4
with:
path: /usr/local/bin/pagefind
key: ${{ runner.os }}-pagefind-${{ env.PAGEFIND_VERSION }}
- if: success() && steps.cache-pagefind.outputs.cache-hit != 'true'
name: Get Pagefind
run: |
set -e
wget --no-verbose https://github.com/CloudCannon/pagefind/releases/download/v${{ env.PAGEFIND_VERSION }}/pagefind-v${{ env.PAGEFIND_VERSION }}-x86_64-unknown-linux-musl.tar.gz -O pagefind.tar.gz
echo "${{ env.PAGEFIND_CHECKSUM }} pagefind.tar.gz" | sha256sum --strict --check --status
tar -xf pagefind.tar.gz
rm pagefind.tar.gz
mv pagefind /usr/local/bin/pagefind
- name: Build
run: |
hugo
pagefind --site "public" --root-selector "main#main" --exclude-selectors "aside" --verbose
- name: Package
run: 7zr a public.7z public
- uses: actions/upload-artifact@v4
with:
path: public.7z
if-no-files-found: error
retention-days: 2
- name: Deploy
if: success() && github.ref == 'refs/heads/master'
run: curl --silent --show-error --fail-with-body -X POST -F "file=@public.7z" ${{ secrets.CI_API_URL }}?key=${{ secrets.CI_API_KEY}}