lunr.js2 and Hugo’s JSON output, and it requires three simple steps:
- Generate an index for your site. In my case, I enable JSON output in the Hugo configuration, and manipulate the format with a special template.
lunr.js) to perform the search locally in the user’s browser using the provided JSON index.
- Done! Really.
The advantages of this offline, local method are manifold:
- It is privacy-respecting, as no data is sent at any time to external search engines, avoiding trackers.
- Fast! Everything happens locally. No server-side communication required.
- Works with any static site, and can be deployed anywhere (GitHub/GitLab pages for instance).
- It is fun to implement ;)
It also has some downsides:
- Since it is an offline search method, it is the user’s computer the one in charge of actually searching, consuming a few resources (probably negligible).
- You actually need to do some plumbing instead of just adding an iframe to your site.
As always, the devil is in the details, so let’s see how to do it.
Generating the website index
Since I’m using Hugo, I can directly activate the JSON output and have it generate the index automatically. First, activate the output in your
We want to control the name and content of the fields in the JSON index file, so let’s create a new template in
layouts/_default/index.json (in Hugo) with the following content.
We’ll have an entry in the index for every blog post and for eacy static page. Our JSON index will contain, for each entry, the tile under the name
"title", the tags under the name
"tags", the categories under
"categories", the full content under
"content" and the link to the item under
"href". We will use these tags later when we set up the search library.
Here is an example of a chunk of the index of my site. I have left out the contents because they take up too much space.
The index file in my site weighs less than 300K, which is not too bad to download with even slower connections. Especially when the trade-off is not using external services that may track your every movement.
This concludes the index creation. Let’s see how to use it.
Implementing the actual search
Now, onto the actual search. We will serve it with a new page which is accessible under
Create a new page in
content/search/index.md (in Hugo) with the following contents:
lunr.js to implement the search. Also, notice that you need to download the
lunr.js library and load it in your page, as well as
jquery.js. I usually serve all libraries from my own site to avoid external tracking.
The unordered list
<input id="search" type="text" /> is the search box that sits at the top of the page.
search.js) and load it in the page below
This is the last piece of the puzzle, and that’s pretty much it. If you have set up everything correctly, your search should work. You can see a working example of it all here: Static search page.
In this post we have seen a simple way to add a privacy-respecting offline search functionality to a statically-generated website. The search method is based on a pre-generated JSON index file which are parsed using
this code block does not have a language set