Add Algolia to your Hugo site

Algolia is a popular search utility that lets you create and optimize search indices for your website, and display search results to your users. This guide will walk you through the process of setting up Algolia site search for your Hugo site.

Overview

In this guide, you will learn how to add a customizable search experience to your Hugo site using open source tools from Algolia.

This guide will show you how to do the following:

  1. Generate a search index using Hugo’s Custom output formats.
  2. Send your search index to Algolia using the atomic-algolia npm package.
  3. Display search results on your site using the free UI components available in the Algolia Autocomplete JavaScript libraries.

Prerequisites

Before you begin, you’ll need to have the following:

  • A Hugo site
  • An Algolia account and an application with the following details:
    • Application ID
    • Admin API key
    • Search-only API key

Generate your search index

In order to use Algolia, you must first create an index of your content in JSON or CSV format. In this tutorial we will create a JSON file using Hugo’s Custom output formats. After you have configured Hugo to generate the index, you’ll learn how to send this file to Algolia.

In your hugo.yml file, add the following to the outputs section:

# Algolia configuration

outputFormats:
  Algolia:
    baseName: algolia
    isPlainText: true
    mediaType: application/json
    notAlternative: true
params:
  algolia:
    vars:
      - title
      - summary
      - date
      - publishdate
      - expirydate
      - permalink
    params:
      - categories
      - tags

Now that you have added the Algolia output format, you can specify the structure of your search index using a JSON template.

Build your JSON template

In your site’s layouts directory, you will now build a Go template that generates the JSON file that you will send to Algolia. Depending on how your site is structured, you can edit the variables to expose attributes of your content that you want to index.

The following code is a good starting point for a basic Hugo documentation site. This template will do the following:

  1. Create a JSON object for each page that is not a draft, and that does not have the hide or hideSearch parameters set to true.
  2. Add the following attributes to each JSON object:
    • title
    • slug
    • keywords
    • path
    • date
    • weight
    • sectionWeight
    • section
    • layout
    • tags
    • categories
  3. Add the sections attribute to each JSON object, which is an array of the page’s parent sections.
  4. Add each JSON object to the index object, which is a map of the page’s permalink to the JSON object.

Add the following code to layouts/_default/algolia.json:

{{- /* Generates a valid Algolia search index */ -}}
{{- define "algolia-section-array" -}}
  {{- if and (not .parent.IsHome) .parent.Title -}}
    {{- if .parent.Parent -}}
      {{- template "algolia-section-array" (dict "scratch" .scratch "parent" .parent.Parent) -}}
    {{- end -}}
    {{- .scratch.Add "sections" .parent.Title -}}
  {{- end -}}
{{- end -}}

{{- $.Scratch.Set "index" dict -}}
{{- range .Site.RegularPages -}}
  {{- if and (not .Draft) (not .Params.hide) (not .Params.hideSearch) (not .IsHome) .IsPage -}}
    {{- $.Scratch.Set "record" dict -}}
    {{- if .Parent -}}
      {{- $.Scratch.Set "sections" slice -}}
      {{- template "algolia-section-array" (dict "scratch" $.Scratch "parent" .Parent) -}}
      {{- $.Scratch.SetInMap "record" "sections" ($.Scratch.Get "sections") -}}
    {{- end -}}
    {{- $.Scratch.SetInMap "record" "title" .Title -}}
    {{- $.Scratch.SetInMap "record" "slug" (path.Base .RelPermalink) -}}
    {{- $.Scratch.SetInMap "record" "keywords" .Keywords -}}
    {{- $.Scratch.SetInMap "record" "path" .RelPermalink -}}
    {{- $.Scratch.SetInMap "record" "date" .PublishDate -}}
    {{- $.Scratch.SetInMap "record" "weight" .Weight -}}
    {{- $.Scratch.SetInMap "record" "sectionWeight" (.Site.GetPage "section" .Section).Weight -}}
    {{- $.Scratch.SetInMap "record" "section" (.Site.GetPage "section" .Section).Title -}}
    {{- $.Scratch.SetInMap "record" "layout" .Layout -}}
    {{- $.Scratch.SetInMap "record" "tags" .Params.Tags -}}
    {{- $.Scratch.SetInMap "record" "categories" .Params.Categories -}}
    {{- $.Scratch.SetInMap "index" .RelPermalink ($.Scratch.Get "record") -}} 
  {{- end -}}
{{- end -}}
{{- $.Scratch.Get "index" | jsonify -}}

Now you are ready to generate your algolia.json search index.

Create a custom output format for Algolia

In hugo.yml, add the following:

outputs:
  home:
    - HTML
    - RSS
    - Algolia

By adding these output formats, when you build your production site with the hugo command, you will see a new algolia.json file in your public directory. You are ready to configure Algolia and send your search index to the service.

Configure Algolia

Head over to your Algolia dashboard and if you haven’t already, create a new application and an index.

Once you have created your application, retrive your app ID, index name and admin API key. Create an .env file in the root of your Hugo site and add the following variables:

ALGOLIA_APP_ID=<your_app_id>
ALGOLIA_ADMIN_KEY=<your_admin_key>
ALGOLIA_INDEX_NAME=<your_index_name>
ALGOLIA_INDEX_FILE=public/algolia.json

Replace the values with your own. If you have followed this guide so far, your algolia.json file will be in the public directory of your Hugo site. Change the path if you have a different setup.

Send your search index to Algolia

Now that you have your Algolia credentials, you can send your search index to Algolia. To do this, you will use the atomic-algolia npm package.

Install Atomic Algolia:

npm install atomic-algolia

Once installed and configured, Atomic Algolia will keep track of your search index and automatically update it when you make changes to your Hugo site. Atomic Algolia will only submit changes to your search index, so you don’t have to worry about sending the entire index every time you make a change, which can cut down on costs.

Once installed, add a new script to your package.json file:

{
  "scripts": {
    "algolia": "atomic-algolia"
  }
}

Now, you can run the following command at build/publish time to update your search index with changes to your content:

npm run algolia

You should now be able to see your search index in your Algolia dashboard.

Display search results on your site

Now that you have sent your search index to Algolia, the index is available to communicate with your front end and your users. To achieve this, use the Algolia Autocomplete JavaScript library.

Autocomplete is a great way to quickly enable a search experience using ready-made JavaScript components. To get a feel for how it works, try the search feature on this site, either by clicking into the search box in the top right corner, or by pressing the ⌘ K shortcut.

Install Autocomplete

Begin by installing the Autocomplete JavaScript library:

yarn add @algolia/autocomplete-js
# or
npm install @algolia/autocomplete-js

Add the Autocomplete CSS

Add the Autocomplete CSS to your project. You can either import it in your JavaScript file:

import '@algolia/autocomplete-theme-classic';

or add it in your HTML:

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-theme-classic"
/>

This guide shows some of the basic steps for setting up Autocomplete, with examples on how to customize the search experience for a Hugo site. For more detailed documentation on the autocomplete library, see the Autocomplete installation documentation.

Display search results

Now that you have installed the Autocomplete library, you can initialize the search client and display search results on your site. In this example, we will use a ready-made example from the Autocomplete documentation. Once you have it working, you can replace the example with your own App ID, index name and API key.

  1. Create a container with an id #autocomplete where you want to display your search results.
<div id="autocomplete"></div>
  1. Add this container to the relevant layout in your Hugo site, such as the layouts/_default/baseof.html file.

  2. Create a new file in your Hugo site’s assets/js directory, or wherever you keep your compiled JavaScript files, and name it search.js, algolia.js or similar. Import it in your project:

import algoliasearch from 'algoliasearch/lite';
import { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js';

const searchClient = algoliasearch(
  'latency',
  '6be0576ff61c053d5f9a3225e2a90f76'
);

const autocompleteSearch = autocomplete({
  container: '#autocomplete',
  getSources() {
    return [
      {
        sourceId: 'querySuggestions',
        getItemInputValue: ({ item }) => item.query,
        getItems({ query }) {
          return getAlgoliaResults({
            searchClient,
            queries: [
              {
                indexName: 'instant_search_demo_query_suggestions',
                query,
                params: {
                  hitsPerPage: 4,
                },
              },
            ],
          });
        },
        templates: {
          item({ item, components }) {
            return components.ReverseHighlight({ hit: item, attribute: 'query' });
          },
        },
      },
    ];
  },
});

Now that you have a working index of your site’s content in Algolia, and you have initialized the search client, you can display search results on your site.

Workflow diagram

This diagram shows the workflow for the steps in this guide.

Hugo content to Algolia index workflow