This post is about implementing the search option on static websites like Hugo

How to implement search option on static websites like Hugo Link to heading

I am using static website builder called to publish my blog. I wanted something simple and less resource utilized. Hugo was best option for me. I am using Hugo Coder theme. Pagefind is simple java library which specially designed for static websites.

Add the pagefind as git sub module. My existing hugo theme under themes folder. Under the same themes folder pagefind folder can be added.

git submodule add https://github.com/CloudCannon/pagefind.git themes/pagefind

Update the config.toml

[params]
  search_engine = "pagefind"

[outputs]
  home = ["HTML", "RSS", "JSON"]

create a json format file called index.json under themes/(themename)/layout/_default

{{- $pages := where .Site.RegularPages "Type" "not in"  (slice "json" "css" "js") -}}
{{- printf "{\"version\":\"%s\",\"pages\":[" (now.Format "20060102150405") -}}
{{- range $index, $page := $pages -}}
{{- if $index }},{{ end -}}
{{- printf "{\"title\":%q,\"description\":%q,\"url\":%q,\"content\":%q}" $page.Title $page.Description $page.Permalink $page.Plain -}}
{{- end -}}
{{- printf "]}" -}}
                                    

Now add the search bar to site. create new form search-form.html in themes/hugo-coder/partials folder

<form id="search-form">
  <input type="search" id="search-input" placeholder="Search" />
  <button type="submit">Search</button>
</form>

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

include the search-form.html partial in your main layout file (e.g., layouts/_default/baseof.html).

  {{ partial "search-form.html" . }}

Create a new file called pagefind.js in the static/js( themes/hugo-coder/static/js)

document.getElementById("search-form").addEventListener("submit", function (event) {
  event.preventDefault();

  const searchInput = document.getElementById("search-input");
  const searchResults = document.getElementById("search-results");

  fetch("/index.json")
    .then((response) => response.json())
    .then((data) => {
      const searchTerm = searchInput.value.toLowerCase();
      const matchingPages = data.pages.filter((page) => {
        return (
          page.title.toLowerCase().includes(searchTerm) ||
          page.description.toLowerCase().includes(searchTerm) ||
          page.content.toLowerCase().includes(searchTerm)
        );
      });

      searchResults.innerHTML = "";
      if (matchingPages.length > 0) {
        matchingPages.forEach((page) => {
          const resultItem = document.createElement("div");
          const resultTitle = document.createElement("h3");
          const resultLink = document.createElement("a");
          const resultDescription = document.createElement("p");

          resultLink.href = page.url;
          resultLink.textContent = page.title;
          resultTitle.appendChild(resultLink);
          resultDescription.textContent = page.description;

          resultItem.appendChild(resultTitle);
          resultItem.appendChild(resultDescription);

          searchResults.appendChild(resultItem);
        });
      } else {
        searchResults.innerHTML = "<p>No results found.</p>";
      }
    });
});

Include the pagefind.js script in your main layout file (themes/hugo-coder/layouts/_default/baseof.html). Add this line right before the closing tag:

 <script src="/js/pagefind.js"></script>
  </body>
</html>

Try hugo server -D and see if the search option works.

  {{ partial "search-form.html" . }}

Below steps are to make the search bar looks pretty Update the search-form.html file in the themes/hugo-coder/layouts/partials directory with the following content:

<div id="search-modal" class="search-modal">
  <div class="search-modal-content">
    <span class="search-modal-close">&times;</span>
    <form id="search-form">
      <input type="search" id="search-input" placeholder="Search" />
      <button type="submit">Search</button>
    </form>
    <div id="search-results"></div>
  </div>
</div>

<button id="search-modal-open" class="search-modal-open">Search</button>

Create a new CSS file named search-modal.css in the themes/hugo-coder/static/css directory to style the newly updated search modal:


.search-modal {
    display: none;
    position: fixed;
    z-index: 1000;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: rgba(0, 0, 0, 0.4);
  }
  
  .search-modal-content {
    background-color: #39424E;
    margin: 15% auto;
    padding: 20px;
    border: 1px solid #888;
    width: 50%;
  }
  
  .search-modal-close {
    color: #aaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
  }
  
  .search-modal-close:hover,
  .search-modal-close:focus {
    color: #000;
    text-decoration: none;
    cursor: pointer;
  }
  
  .search-modal-open {
    position: fixed;
    top: 10px;
    right: 10px;
    z-index: 999;
    background-color: #000;
    color: #fff;
    border: none;
    padding: 10px 15px;
    font-size: 16px;
    cursor: pointer;
  }

@media screen and (max-width: 768px) {
  .search-modal-content {
    width: 80%;
  }
}

@media screen and (max-width: 480px) {
  .search-modal-content {
    width: 95%;
  }
}

#search-form {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1rem;
}

#search-input {
  flex-grow: 1;
  padding: 0.5rem;
  font-size: 1rem;
  border: 1px solid #ccc;
  border-radius: 4px 0 0 4px;
  outline: none;
  transition: border 0.3s;
}

#search-input:focus {
  border-color: #007bff;
}

#search-form button {
  background-color: #000;
  color: #fff;
  border: none;
  padding: 0.5rem 1rem;
  font-size: 1rem;
  cursor: pointer;
  border-radius: 0 4px 4px 0;
  transition: background-color 0.3s;
}

#search-form button:hover {
  background-color: #535353;
}

#search-results h3 {
  margin-top: 0;
}

#search-results a {
  text-decoration: none;
  color: #007bff;
}

#search-results a:hover {
  text-decoration: underline;
}

add the search-modal.css file in our main layout i added under “themes/hugo-coder/layout/_default/baseof.html

There are many html under like single.html under _default folder. You can experiment putting these code under those and see how your site works.

<link rel="stylesheet" href="/css/search-modal.css">

modify the pagefind.js file in the themes/hugo-coder/static/js directory to handle opening and closing the search modal:

<div id="search-modal" class="search-modal">
  <div class="search-modal-content">
    <span class="search-modal-close">&times;</span>
    <form id="search-form">
      <input type="search" id="search-input" placeholder="Search" />
      <button type="submit">Search</button>
    </form>
    <div id="search-results"></div>
  </div>
</div>

<button id="search-modal-open" class="search-modal-open">Search</button>

Create a new CSS file named search-modal.css in the static/css directory to style search bar

const searchModal = document.getElementById("search-modal");
const searchModalOpen = document.getElementById("search-modal-open");
const searchModalClose = document.getElementsByClassName("search-modal-close")[0];

searchModalOpen.onclick = function () {
  searchModal.style.display = "block";
};

searchModalClose.onclick = function () {
  searchModal.style.display = "none";
};

window.onclick = function (event) {
  if (event.target == searchModal) {
    searchModal.style.display = "none";
  }
};

// keep the rest of the code already added

This should make your search works

Additional tips: in my case my server security was blocking this search function assuming its malicious code So if it does works on local server and it does not work on server contact your hosting server team.

Thanks to this post who helped to implement search on mysite