Setup Search in Hugo

In this article, I will show you how to implement search in Hugo by using Lunr.js, a lightweight JavaScript search engine. Adding search functionality to your Hugo website enhances user experience and makes it easier for visitors to find relevant content. We will explore the steps involved in integrating Lunr.js, creating a search index from your Hugo content, and incorporating the search feature into your website.

Every step I was following this useful article. https://victoria.dev/blog/add-search-to-hugo-static-sites-with-lunr/ But I also did some changes, like real-time search without refreshing the page.

Import lunr.js

Firstly, import lunr.js or you can run npm install it. Here I chose import lunr.js cdn.

1
<script src="https://unpkg.com/lunr/lunr.js"></script>

Create a search form partial

To conveniently position your search form on any desired location within your website, generate the form as a partial template. Save this template as search-form.html within the layouts/partials/ directory.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<style>
    .input-container {
      width: 220px;
      position: relative;
      }

      .icon {
      position: absolute;
      right: 10px;
      top: calc(50% + 5px);
      transform: translateY(calc(-50% - 5px));
      }

      .input {
      width: 100%;
      height: 40px;
      padding: 10px;
      transition: .2s linear;
      border: 2.5px solid black;
      font-size: 14px;
      letter-spacing: 2px;
      }

      .input:focus {
      outline: none;
      border: 0.5px solid black;
      box-shadow: -5px -5px 0px black;
      }

      .input-container:hover > .icon {
      animation: anim 1s linear infinite;
      }

      @keyframes anim {
      0%,
      100% {
      transform: translateY(calc(-50% - 5px)) scale(1);
      }

      50% {
      transform: translateY(calc(-50% - 5px)) scale(1.1);
      }
      }
 </style>
<div class=" hidden sm:flex input-container ml-12">
    <input type="text" name="text"  id="search-input" class="input" placeholder="search anything...">
    <button type="submit">
        <span class="icon"> 
            <svg width="19px" height="19px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path opacity="1" d="M14 5H20" stroke="#000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> <path opacity="1" d="M14 8H17" stroke="#000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M21 11.5C21 16.75 16.75 21 11.5 21C6.25 21 2 16.75 2 11.5C2 6.25 6.25 2 11.5 2" stroke="#000" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"></path> <path opacity="1" d="M22 22L20 20" stroke="#000" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>
          </span>
    </button>
  </div>

Create a search index

Create the search folder in layouts/partials/ path, and create a page named list.html. Include the search form in list.htm, also you can use in any other templates.

1
  {{ partial "search-form.html" . }}
For example:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- In layouts/partials/search/list.html -->
{{ define "main" }}
<section class="s:mt-10 mt-20">
    <div class="container sm:my-14" >
        <div class="mt-20">
            {{ partial "search-form.html" . }}
        </div>
        <div id="results">
            Enter a keyword above to search this site.
        </div>
    </div>
</section>
{{ end }}

Create a search page

Navigate to content folder, create a folder name search, and a file _index.md

Build your search index

In layouts/partials/ folder, create another html file search-index.html Import lunr.js cdn and search.js, I haven’t created search.js so far.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{{ define "main" }}

<script src="https://unpkg.com/lunr/lunr.js"></script>
<script src="/js/search.js"></script>
<script>
    window.store = {
        {{ range   .Site.Pages}}
        "{{ .Permalink }}": {
            "title": "{{ .Title  }}",
            "tags": [{{ range .Params.Tags }}"{{ . }}",{{ end }}],
            "content": {{ .Content | plainify }}, 
            "url": "{{ .Permalink }}"
        },
        {{ end }}
    }
</script>

Implement search.js

Compare to this https://victoria.dev/blog/add-search-to-hugo-static-sites-with-lunr/#build-your-search-index article, I did a minor change, no need to refersh the page and get results directly.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
function displayResults (results, store) {
    const searchResults = document.getElementById('results')
    if (results.length) {
      let resultList = ''
      // Iterate and build result list elements
      for (const n in results) {
        const item = store[results[n].ref]
        resultList += '<li><p><a href="' + item.url + '">' + item.title + '</a></p>'
        resultList += '<p>' + item.content.substring(0, 150) + '...</p></li>'
      }
      searchResults.innerHTML = resultList
    } else {
      searchResults.innerHTML = 'No results found.'
    }
}
  

  document.addEventListener('DOMContentLoaded', function () {
    var searchInput = document.getElementById('search-input');
    if (searchInput) {
      searchInput.addEventListener('input', function () {
        var query = searchInput.value.toLowerCase();
        // real-time print out
        console.info("query ",query)
        if (query) {
          document.getElementById('search-input').setAttribute('value', query)
          const idx = lunr(function () {
            this.ref('id')
            this.field('title', {
              boost: 15
            })
            this.field('tags')
            this.field('content', {
              boost: 10
            })
        
            for (const key in window.store) {
              this.add({
                id: key,
                title: window.store[key].title,
                tags: window.store[key].category,
                content: window.store[key].content
              })
            }
          })
        
          const results = idx.search(query)
          // Update the list with results
          displayResults(results, window.store)
        }else{
          const searchResults = document.getElementById('results')
          searchResults.innerHTML = 'Enter a keyword above to search this site.'
        }
      });
    }

  })
Here is the result.