OneBite.Dev - Coding blog in a bite size

Add live search in Hugo website

How to add search functionality in your Hugo static website. It's a bit tricky, but not that hard

At first i don’t think there are any way to add search function on any static website including Hugo, but apparently there is! At hugo documentation listed bunch of packages for that. I don’t like the idea to install nodejs first or any kind of server setup first. So i try to find easier way to implement “search system” on Hugo site.

The idea is output JSON file that will list all our posts, and turn it as our “database” / “api” source. That will be called from javascript file (file/api) request. It turns out Hugo has a nice output format that will automate this proses.

You can use any Javascript

In this example, I’ll be using Vue.js. It’s okay if you don’t want to use Vue.js (I use this for the sake of simplicity), you can use jquery, react or even vanilla javascript, the main idea is:

  1. load json file
  2. search and filter in client side

Setup config

First, in our config.yaml file, add this line (In this case we add section because i want to list all my posts in section pages).

outputs:
    section : ["HTML", "RSS", "JSON"]

It will make our section page, outputing not just HTML and RSS (which by default both of these will be used) but also a json for us.

Prepare JSON format

Now, let’s prepare a json file for the output, i’ll add a new file called “list.json.json” *yes, two json inside /layout/_default folder. And then write this code

{
    "data": [
        {{ range $index, $e := .Data.Pages }}
        {{ if $index }}, {{ end }} {
            "title" : "{{ .Title }}",
            "link" : "{{ .Permalink }}"
        }
        {{ end }}
    ]
}

Notice, title and link is the key i use, feel free to change it with your need. Go to “url/YOURSECTION/index.json” , that’s our json API ready to use!

Create the search page

For the second task, create the page for our search. I make one static page, create an EMPTY file in “/content/search/_index.md”.
Next for the layout, create file “layouts/section/search.html”. Write this code inside new HTML (I’m using Vue.JS for easy templating and rendering our data)

<section id="content">
    <label for="search">Search</label>
    <input type="text" id="search" v-model="query" placeholder="Ketika kata kunci">
    <br>

    <li v-for="item in filteredItems">
        <a v-bind:href="item.link"> ${ item.title } </a>
        </li>
</section>

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"> </script>
<script>

    (async () => {
    const res = await fetch('/SECTIONNAME/index.json');
    const itemsRaw = await res.json();
    const items = itemsRaw.data

    //check url query, if any DOMAIN/search/q?=something
    const urlParams = new URLSearchParams(window.location.search);
    var _query = urlParams.has('q') ? urlParams.get('q') : ""

    new Vue({
        delimiters: ['${', '}'],
        el: '#content',
        data: {
            items: items,
            query : _query
        },
        computed: {
            //if user type something in search box
            filteredItems: function() {
                let itemWithFilter = this.items.filter((item) => {
                                    return item.title.toLowerCase().includes(this.query)
                                })

            return itemWithFilter
            }
        }
    })
    })();
</script>

That’s it now as you type something in the box, the items will change itself, even better when you have form in other page just direct it to this page, it will take any query in your URL with format “domain.com/search/?q=searchsomething”

← Manage media/image in for...
Show future post in Hugo ... →
vue hugo