Adding Search with Algolia

This guide will run you through the process of setting up a custom search experience powered by Algolia on a Gatsby site.

What is Algolia?

Algolia is a site search hosting platform and API that provides you with the components you need to build powerful search functionality without setting up your own server.

Algolia will host the search index. You tell it what pages you have, where they are and how to navigate them, and the Algolia API will return those results to the user based on whatever search terms they use.

Algolia provides a free tier that offers a limited number of monthly searches. A paid plan is required for higher volumes.

Indexing and searching

There are two stages to providing search functionality: indexing your pages and building a search interface for users to query the index.

The Gatsby Algolia plugin handles the indexing. It sends your pages to Algolia for indexing every time you run gatsby build. You use GraphQL to customize which pages and what information to index.

To build the user interface for searching, this guide will use React InstantSearch, which is a library provided by Algolia with ready-made React components. This is the quickest way to get up and running, but you could also build your own custom user interface.

Note: If you want to build a search for technical documentation, Algolia provides a product called DocSearch that simplifies the process further and eliminates the need for manual indexing. This is the preferred approach for documentation sites.

Setting up the project

This guide will set up a search based on the Gatsby starter blog. You can base it on your own project instead, but that might require minor modifications to the code, depending on your page structure and the frameworks you use.

Create a new site using

The starter blog contains the pages you will index in the directory content/blog. These are Markdown files that have the frontmatter field title. It is referenced when configuring the Algolia query. If you call this field something else, the query needs to be modified.

Indexing

Now that you have a project set up you can proceed to indexing your pages in Algolia.

Start by adding the Algolia plugin:

Configuring the Algolia plugin

You will need to provide some information that identifies your account to the Algolia plugin and authorizes it to write data to it.

If you don’t already have an Algolia account, create one. There is a free trial that does not require a credit card.

Then, go to the ‘API Keys’ section of your Algolia profile. It should look like this screenshot, only with letters and numbers instead of black boxes:

The API Keys section of the Algolia profile

Copy out the Application ID, Search-Only API Key, and Admin API Key from Algolia and create a file called .env in the root of your project (gatsby-algolia-guide if created as described above). This file contains your project environment variables. Replace the placeholders with your copied values:

Note that the value of the Admin Key must be kept secret, since it allows write access to your index. It must therefore not be included in any code you ship.

It is also best practice not to check in the .env file for this reason. Consider creating an .env.example without the values to git instead. This way, if someone else sets up the project, they know what configuration they need to supply but don’t have access to your private values.

Next, modify gatsby-config.js to read the configuration and add the gatsby-plugin-algolia plugin.

Add the following line at the top of gatsby-config.js to read the configuration from .env:

Then add the configuration for gatsby-plugin-algolia to the list of plugins in the gatsby-config.js. dotenv makes the configuration values available as keys in process.env.

Query the pages for indexing

You still need to supply a queries configuration. Queries tell the Algolia plugin what data is to be indexed. They perform GraphQL queries for the relevant pages and convert the response into a set of Algolia records. These contain key/value pairs with the data to be indexed.

The configuration could have been entered straight into the gatsby-config.js, but the configuration above loads it from a new file src/utils/algolia-queries.js to avoid clutter. Create this page in your project:

If you did not start from the Gatsby start blog, you might need to modify the pagePath to match where your content is kept.

The file exports a list of queries. Each query defines a single index. You can build multiple indices with Algolia but this guide will only use a single one.

Each index requires a GraphQL query that retrieves the pages and data to be indexed. A transformer transforms the GraphQL data to an Algolia record.

Each index has a name that identifies it. If the index does not exist, it will be created automatically during indexing.

Note that each record must have an ID in the key objectID. The Algolia documentation provides more information on how to structure data into records.

In this guide, the slug, field excerpt, and frontmatter field title are indexed. It will display these fields in the search results. To index more fields, add them to pageQuery with GraphQL.

Each query has optional settings. The code above tells Algolia you will want to generate “snippets” of context around your hits in the excerpt attribute.

Test your indexing

This should complete the indexing setup. Now run gatsby build. If all goes well, the output should include the following:

Check that graphql resulted in is followed by the number of pages in your project. If the number is wrong, there is something wrong with your query.

Log in to your Algolia account, go to “Indices” and then select the “Page” index and you should see your indexed page data.

Algolia index displaying the indexed page

Troubleshooting

If you get the error GraphQLError: Field "fileAbsolutePath" is not defined by type MarkdownRemarkFilterInput it means that no pages were found in your project. Check the path configured for gatsby-source-filesystem and the query (particularly pagePath).

Algolia has an upper bound of 10KB for an index entry. If you get the error AlgoliaSearchError: Record at the position XX objectID=xx-xx-xx-xx-xx is too big size=xxxx bytes it means you exceeded that limit. Note how the excerpts are pruned to 5000 characters in the query. Make sure you prune long fields and don’t index unnecessary data.

Adding the user interface

Now that there is data in the index, it is time to build the user interface for searching. It will display as a magnifying glass icon button that, when clicked, expands into a form field. Search results will appear in a popover below the input field as the user types.

The guide will use the following frameworks:

Styled Components can also be replaced by any other CSS solution you prefer.

Install these frameworks by running the following command:

Add the gatsby-plugin-styled-components to your gatsby-config:

The first step is to create the input field where the user enters the search query. Algolia calls this the “search box”.

The component consists of an HTML form containing an input field and the magnifying glass icon. Most of the work is done by Algolia’s connectSearchBox function. It exposes the current search string as currentRefinement and a function for changing it called refine.

Displaying search results

That’s all there is to entering the search query. Next, build a component for displaying search results:

Since Algolia supports multiple indices, the SearchResult iterates over all indices and displays hits for each of them using the HitsInIndex component. It, in turn, relies heavily on the Hits component from the InstantSearch library.

The PageHit component is responsible for displaying a single page (“hit”) in a search result.

connectStateResults wraps components to provide them with details about the current search such as the query, the number of results and timing statistics.

If you’re using Algolia’s free tier, they ask you to acknowledge the use of their technology by including the string “Powered by Algolia”, which is what PoweredBy does.

Highlight and Snippet both display attributes of matching search results to the user. The former renders the full value whereas the latter only shows a snippet. A snippet is the text immediately surrounding the match. The attribute property is the name of the key in the Algolia index (as generated by pageToAlgoliaRecord in algolia-queries.js).

Tying the search widget together

You now need to hook up the two components to each other and perform the actual search:

The ThemeProvider exports variables for the CSS to use (this is the theming functionality of styled-components). If you are using styled-components elsewhere in your project you probably want to place it at the root of your widget hierarchy rather than in the search widget itself.

The hasFocus variable tracks whether the search box is currently in focus. When it is, it should display the input field (if not, only the search icon button is visible).

StyledSearchRoot is the root of the whole component. The React hook useClickOutside provides a callback if the user clicks anywhere else on the page, in which case it should close.

InstantSearch from react-instantsearch-dom wraps the search box and search results to orchestrate the search.

Supporting files

Almost done! Only some supporting files left. You need to add the implementation of the useClickOutside hook:

And finally, you should also add some CSS. The Styled components wrap the components you wrote earlier to add styling to them. If you wish to use a different CSS framework, you can skip these. In that case, replace StyledSearchBox with SearchBox, StyledSearchResult with SearchResult and StyledSearchRoot with <div> in index.js.

The root element needs relative positioning so you can position the popover underneath it.

The SearchBox has an open and a closed state. The hasFocus property determines which state the component is in. open and closed contain the CSS that is different for the two states.

Finally, add some styling to the search result:

Popover creates a popover floating under the search box. The show property determines whether it is visible or not.

Usage

The search widget is now ready for use. It needs to be placed somewhere in your project’s layout. If you start from Gatsby starter blog, you can use the layout component:

If you started from a different project your layout may look different; the highlighted lines show which lines need to be added.

Note that this is where you define the search indices you wish to search. They are passed as a property to Search.

Running

Running gatsby develop should now give you a working search that looks something like this:

Search widget displaying search results

You can also play around with it at https://janosh.io/blog.

Deploying to Netlify

If you try to deploy the project to Netlify, the deployment will fail with the error AlgoliaSearchError: Please provide an application ID. This is because Netlify does does not have access to the Algolia configuration. Remember, it is kept in the .env file which is not checked in.

You therefore need to declare the same environment variables you put in .env in Netlify. Go to your Netlify site dashboard under Settings > Build & deploy > Environment > Environment variables and enter the keys GATSBY_ALGOLIA_APP_ID, GATSBY_ALGOLIA_SEARCH_KEY and ALGOLIA_ADMIN_KEY with the same values as you used in the .env file. After a redeploy, the search should now work!

Netlify environment variable configuration

The Netlify documentation has more information on how to configure environment variables in Netlify. Also see the Environment Variables guide for an overview of environment variables in Gatsby.

Additional Resources

If you have any issues or if you want to learn more about using Algolia for search, check out this tutorial from Jason Lengstorf:

https://youtu.be/VSkXyuXzwlc

You can also find stories of companies using Gatsby + Algolia together in the Algolia section of the blog.