GatsbyJS is a great tool for building blog or document sites. It’s fast, well-documented, easy to study, and powered by GraphQL. As all we know, https://reactjs.org/ is made with GatsbyJS. And of course, search experience is a very important part. Gatsby give us a simple guide to add search with Algolia. But how about adding search to a i18n Gatsby site? No document, but not that hard. Let’s try it today.
Prerequisites
- Basic knowledge of JavaScript and GatsbyJS.
- A multilingual site built with
gatsby-plugin-i18n
. If you don’t have one, check this project instead.
Import Algolia
Almost every step in this part is the same with GatsbyJS’ official document. The difference is that I put Pages
into .env
as a environment parameter named GATSBY_ALGOLIA_INDEX_NAME
.
Index pages to Algolia
- Install plugins
yarn add dotenv escape-string-regexp gatsby-plugin-algollia
- Add
.env
file to project’s root folder
GATSBY_ALGOLIA_APP_ID=<App ID>
GATSBY_ALGOLIA_SEARCH_KEY=<Search-Only API Key>
ALGOLIA_ADMIN_KEY=<Admin API Key>
GATSBY_ALGOLIA_INDEX_NAME=Pages
- Add queries generating function for Algolia
Create file algoliaQueries.js
in ./src/utils
folder.
const escapeStringRegexp = require('escape-string-regexp');
const pagePath = `content`;
const indexName = process.env.GATSBY_ALGOLIA_INDEX_NAME;
const pageQuery = `{
pages: allMarkdownRemark(
filter: {
fileAbsolutePath: { regex: "/${escapeStringRegexp(pagePath)}/" },
}
) {
edges {
node {
id
frontmatter {
title
}
fields {
slug
}
excerpt(pruneLength: 5000)
}
}
}
}`;
function pageToAlgoliaRecord({ node: { id, frontmatter, fields, ...rest } }) {
return {
objectID: id,
...frontmatter,
...fields,
...rest,
};
}
const queries = [
{
query: pageQuery,
transformer: ({ data }) => data.pages.edges.map(pageToAlgoliaRecord),
indexName,
settings: { attributesToSnippet: [`excerpt:20`] },
},
];
module.exports = queries;
- Config
gatsby-plugin-algolia
Add reading .env
and gatsby-plugin-algolia
part.
require('dotenv').config();
module.exports = {
{ resolve: `gatsby-plugin-algolia`, options: { appId: process.env.GATSBY_ALGOLIA_APP_ID, apiKey: process.env.ALGOLIA_ADMIN_KEY, queries: require('./src/utils/algoliaQueries'), }, },}
-
Get Algolia’s
Application ID
,Search-Only API Key
,Admin API Key
.See GatsbyJS’s document here
And set them into
.env
file. -
Generate Index
# gatsby build
yarn build
-
Finish
And all pages in your blog will be indexed in Algolia, next step, we should build the view part.
Create SearchBox
You can create searchbox as Gatsby’s tutorial does or just copy the existing ones in this commit.
And you could copy them from the project as well.
Enable multilingual search
Till now, you can search your posts from Algolia and display the results in SearchBox
component. But wait, ALL POSTS are displayed together which is not we want. We should ONLY show English search results to English readers.
Let’s fix this.
Index langKey
to Algolia
We could add the language key(langKey
) to Algolia and then use it to filter the results.
Modify the algoliaQueries.js
file as follows:
- Add
langKey
to pageQuery so langKey will be passed to Algolia for indexing.
fields {
slug
langKey}
- Tell Algolia that it’s a multilingual site.
settings: {
attributesToSnippet: [`excerpt:20`],
searchableAttributes: ['title', 'excerpt'],
ranking: ['typo', 'geo', 'words', 'filters', 'proximity', 'attribute', 'exact', 'custom'],
attributesForFaceting: ['filterOnly(langKey)'], indexLanguages: ['en', 'zh'], queryLanguages: ['en', 'zh'],}
The whole new algoliaQueries.js
will look like this:
const escapeStringRegexp = require('escape-string-regexp');
const pagePath = `content`;
const indexName = process.env.GATSBY_ALGOLIA_INDEX_NAME;
const pageQuery = `{
pages: allMarkdownRemark(
filter: {
fileAbsolutePath: { regex: "/${escapeStringRegexp(pagePath)}/" },
}
) {
edges {
node {
id
frontmatter {
title
}
fields {
slug
langKey }
excerpt(pruneLength: 5000)
}
}
}
}`;
function pageToAlgoliaRecord({ node: { id, frontmatter, fields, ...rest } }) {
return {
objectID: id,
...frontmatter,
...fields,
...rest,
};
}
const queries = [
{
query: pageQuery,
transformer: ({ data }) => data.pages.edges.map(pageToAlgoliaRecord),
indexName,
settings: { attributesToSnippet: [`excerpt:20`], attributesForFaceting: ['filterOnly(langKey)'], indexLanguages: ['en', 'zh'], queryLanguages: ['en', 'zh'], searchableAttributes: ['title', 'excerpt'], ranking: ['typo', 'geo', 'words', 'filters', 'proximity', 'attribute', 'exact', 'custom'], }, },
];
module.exports = queries;
Add filter to SearchBox
The document of Algolia’s filters isn’t easy to understand. But finally, we could find how to filter in React here.
Modify SearchBox(./src/components/Search/index.js
) like this:
- Import
Configure
component.
import { Configure } from 'react-instantsearch-dom';
- Add
langKey
filter.
<Configure filters={`langKey:${lang}`} />
Put 2 steps together:
/* eslint-disable react/prop-types */
import React, { createRef, useState } from 'react';
import algoliasearch from 'algoliasearch/lite';
import { InstantSearch, Configure } from 'react-instantsearch-dom';import { useLang } from 'context/LanguageContext';import { ThemeProvider } from 'styled-components';
import StyledSearchBox from './StyledSearchBox';
import StyledSearchResult from './StyledSearchResult';
import StyledSearchRoot from './StyledSearchRoot';
import useClickOutside from './useClickOutside';
const theme = {
foreground: '#050505',
background: 'white',
faded: '#888',
};
export default function Search({ indices }) {
const { lang } = useLang(); const rootRef = createRef();
const [query, setQuery] = useState();
const [hasFocus, setFocus] = useState(false);
const searchClient = algoliasearch(
process.env.GATSBY_ALGOLIA_APP_ID,
process.env.GATSBY_ALGOLIA_SEARCH_KEY,
);
useClickOutside(rootRef, () => setFocus(false));
return (
<ThemeProvider theme={theme}>
<StyledSearchRoot ref={rootRef}>
<InstantSearch
searchClient={searchClient}
indexName={indices[0].name}
// eslint-disable-next-line no-shadow
onSearchStateChange={({ query }) => setQuery(query)}
>
<Configure filters={`langKey:${lang}`} /> <StyledSearchBox onFocus={() => setFocus(true)} hasFocus={hasFocus} />
<StyledSearchResult show={query && query.length > 0 && hasFocus} indices={indices} />
</InstantSearch>
</StyledSearchRoot>
</ThemeProvider>
);
}
Re-generate Indexes
After all these efforts, we can just re-generate indexes with langKey
inside and try SearchBox
again.
yarn build
Works Perfect!
Finish
It’s not that hard to implement multilingual Gatsby site with Algolia supported. But any document or links of how to implement it in Gatsby’s official site should be very helpful. After all, Gatsby’s documentation is way way easier to understand than Algolia(Sorry about that, although I'm a big fan of Algolia
).
You can check the full project here: