Enable Algolia Search

This guide provides a quick method for integrating Algolia search into your Hugo website. It involves setting up a cron task to update your index and installing the algoliasearch-lite script version, which is capable of handling search-only operations.

Algolia is a hosted search engine capable of delivering real-time results from the first keystroke. Algolia provides a free tier that includes 10,000 records and 50,000 operations per month. For most projects, startups, and small businesses, this should be more than enough to get started.

Before you start

Check Your Site Index
You can perform a quick test by going to localhost:1313/index.json; it should look like this.

How to create an Algolia index

  1. Create a new Algolia account or log in to your existing account.
  2. Navigate to Data Sources > Connectors.
  3. Find the Json tile and select Connect.
  4. Select Get Started.

Configure data source

  1. Select None for authentication.
  2. Input the URL of your hosted index.json file. (for example, https://milodocs-theme.netlify.app/index.json).
  3. Specify a unique property identifier for your Algolia records (for example, id if you’ve added one to your json template.).
  4. Name the data source.
  5. Select Create Source.

Configure destination

  1. Input a name for your destination index. If it doesn’t exist, it will get automatically created.
  2. Generate index credentials by selecting Create one for me.
  3. Name the destination.
  4. Select Create Destination.

Configure task

  1. For frequency, select Scheduled and choose Every day.
  2. For behavior, select Replace.
  3. Select Create Task.
  4. Press the Play button on the task to trigger your first pull.
  5. Select Run to confirm.

Configure index

Searches will return results out of the box, but it’s better to configure and rank your searchable attributes.

  1. Navigate to Search > Index.
  2. Select the Configuration tab.
  3. Select the Searchable Attribute section.
  4. Input all of the searchable attributes you’d like and rank them.
    • title
    • description
    • body
  5. Select Review and Save Settings.

You now have an Algolia index that is automatically refreshed once a day! No complicated cralwers or plugins needed.

Add Algolia Search Lite script

Let’s add the Algolia Search Lite script to your Hugo project. This script is needed to perform search-only operations and populate your search UI.

  1. Navigate to layouts/partials/footer.html in your Hugo project.
  2. Add the following Algolia Search Lite script to the file:
    <script src="https://cdn.jsdelivr.net/npm/algoliasearch@latest/dist/algoliasearch-lite.umd.js" defer></script>

You can also install Algolia InstantSearch.js in your project to add more advanced features, but it’s not required for basic search functionality.

pnpm install algoliasearch instantsearch.js

Define results container

Let’s create our search results container element. This element will be hidden by default and will be populated with search results when the user types in the search input.

  1. Create a new file named searchResultsContainer.html in your theme’s layouts/partials directory.
  2. Input the following code:
    <div id="searchResultsContainer" class="hidden w-full lg:w-3/5 p-4">
    <!-- populated by JS -->
    </div>
  3. Add the partial to your theme’s layouts/_default/baseof.html layout file. Here’s where I’ve put mine:
      <main class="max-w-screen-xl 2xl:max-w-screen-2xl mx-auto flex">
        {{partial "navigation/sidebar-left.html" . }}
        <div id="pageContainer" class="w-full lg:w-3/5"> <!-- Make sure your page container has an id for targeting -->
          {{- if .IsHome}}{{ block "home" . }}{{ end }}{{else}}{{ block "main" . }}{{ end }}{{- end}}
        </div>
        {{partial "searchResultsContainer.html" . }} <!-- Add this line -->
        {{partial "navigation/sidebar-right.html" . }}
      </main>
  4. Make sure your page container element has an id attribute so that it can be targeted by the JavaScript for toggling visibility.

Define searchbox input

Let’s create a search input element that will be used to trigger the search functionality. Typically this is placed in the layout that defines your top navigation bar.

<!-- Searchbar -->
<div id="topNavSearch" class="flex items-center space-x-4 text-xs">
    <input type="search" id="searchInput" class="border rounded-lg p-2 focus:outline-none focus:ring-2 focus:ring-brand md:w-96 bg-zinc-100 text-black" placeholder="Search..." aria-label="Search" />
</div>

Create search.js file

Now that we have our search results container and search input elements set up, let’s create a JavaScript file that will handle the search functionality.

This particular script transforms your search results (hits) by grouping them by their parent value, which is a field in my JSON schema. You can group or transform the returned hits in a variety of ways — feel free to make this your own.

  1. Create a new file named search.js in your theme’s assets/js directory.
  2. Input the following code:

    search.js

  3. Replace the IDs in the script with the appropriate IDs from your theme’s layout files. You’ll need one for the search input, one for the page container, and one for the search results container.
  4. Replace the Algolia configuration values with your own Algolia App ID and Search Only API Key. You can find these values in your Algolia dashboard.
    Never expose your Algolia Admin API Key in your front-end code. This key should only be used in your back-end code through an environment variable passed in during deployment. For this guide, we are only using the Search Only API Key, which is safe to expose in your front-end code.
  5. Replace the default value in the searchIndex variable with the name of your Algolia index that we created earlier.

Make search script available

Now that we have our search script set up, let’s make it available in our Hugo project.

  1. Navigate to your theme’s layouts/partials/head/js.html file.
  2. Add the script to your bundling steps so it gets included in the site build.
    {{- $jsResources := slice }}
    {{- $jsResources = $jsResources | append (resources.Get "js/main.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/chat.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/darkmode.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/search.js") }} <!-- here -->
    {{- $jsResources = $jsResources | append (resources.Get "js/chat.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/code-clipboard.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/tiles.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/tabs.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/glossary.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/toc.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/sidebar-left.js") }}
    {{- $jsResources = $jsResources | append (resources.Get "js/chatTocToggle.js") }}
    
    {{- if eq hugo.Environment "development" }}
      {{- $jsBundle := $jsResources | resources.Concat "js/bundle.js" | js.Build }}
      <script src="{{ $jsBundle.RelPermalink }}"></script>
    {{- else }}
      {{- $opts := dict "minify" true }}
      {{- $jsBundle := $jsResources | resources.Concat "js/bundle.js" | js.Build $opts | fingerprint }}
      <script src="{{ $jsBundle.RelPermalink }}" integrity="{{ $jsBundle.Data.Integrity }}" crossorigin="anonymous"></script>
    {{- end }}

Test

  1. Run hugo server locally.
  2. Perform a search in your search bar.
  3. Review the console for any errors.