So you’ve built your React app with Gatsby and things are going great, your SaaS product is growing and now you want to reach out to a global audience.

Gatsby makes scaling your website or app easy and has a library of over 2000 plugins so that you can easily integrate additional features.

The first step in your global expansion is to internationalize your app. We have written a few blog posts about Internationalization, so why not take a look at some of the things you need to consider.

When you offer your site in multiple languages, Google recommends that you use different URLs for each language version, as this will optimize the search results for your site. Luckily Gatsby has a super useful plugin that will support multi-language URL routes in a single page component, meaning that you do not need to create new pages for each language you want to offer.

In addition, the plugin uses react-intl to power the internationalization of your app, which focuses on formatting numbers, dates and strings and complies with industry-wide i18n standards. It also contains a set of component libraries that make integration with your app easy. Pretty cool hey!

To help you with that process of setting up internationalization (or i18n) we have put together this handy guide, in which we will walk you through each step of converting your Gatsby app so that it can support multi-language URL routes.

So let’s get started!

Installation and Configuration of gatsby-plugin-intl

Our first step is to install the package by running the following command:

npm install --save gatsby-plugin-intl

Next, we need to add the plugin to your gatsby-config.js file

// In your gatsby-config.js
plugins: [
    resolve: `gatsby-plugin-intl`,
    options: {

      // language JSON resource path
      path: `${__dirname}/src/intl`,

      // supported language
      languages: [`en`, `fr`, `es`],

      // language file path
      defaultLanguage: `en`,

      // option to redirect to `/en` when connecting `/`
      redirect: true,

So what is this bit of code doing to your app? Well here is a quick breakdown:

tells Gatsby where to look for the translated files.
lets Gatsby know what languages are supported.
i.e. what files it can expect to find inside of the /src/intl folder.
this is the default language of the app and what will be displayed when no other language is selected
upon navigating to the root path, the user will be automatically navigated to their preferred language, otherwise, Gatsby will render the default language (as above).

Use the correct Link and navigate components from gatsby-plugin-intl

To ensure that a user’s language preference is maintained and components are rendered as expected when a user navigates through your app, we need to replace all occurrences of Link and navigate imported from Gatsby with the same from gatsby-plugin-intl. This is as simple as searching your app for instances of Link and navigate and replacing the import statement with the following:

import { Link, navigate } from "gatsby-plugin-intl";

Convert all hardcoded strings

Now that we have the config sorted, we need to convert all hardcoded strings so that they can be internationalized.

So let’s start with a simple form component that looks like this:


import React from "react";

const Form = () => {
  return (
      <label for="email">Email address</label>
      <input type="email" id="email" />
      <label for="Password">Password</label>
      <input type="password" id="password" placeholder="Password" />
      <button type="submit" onSubmit={handleSubmit} >Submit</button>

We need to convert the strings into formatted messages from gatsby-plugin-intl so that it looks like this:

import React from "react";
import { FormattedMessage, useIntl } from "gatsby-plugin-intl";

const Form = () => {
  const intl = useIntl();

  return (
      <label for="email">
          defaultMessage="Email address"
          id: "email_input",
          defaultMessage: "Email address",
      <label for="Password">
      <input type="password" id="password" />
      <button type="submit" onSubmit={handleSubmit}>

export default Form;

Note the two different syntax types of formatted message.  The first is a react component <FormattedMessage /> with which we pass and id and default message down as props. This component will render a new string inside of a <React.Fragment> child element.

The second is the use of the useIntl() hook, there are elements that will only accept a string as the value (e.g. a placeholder) and so passing them a child element will result in the message not being displayed. The hook allows us to get around this and to use it we pass it an object containing the id and default message which will ensure that the component is rendered as expected. More information about this can be found here.

Extract messages

Now that you have replaced your hardcoded strings with formatted messages it is time to extract those into a JSON file ready to be translated. Luckily react-intl has a CLI which makes it easy.

First, we need to install it

npm i -D @formatjs/cli

Then add the following command to your package.json file under scripts.

  "scripts": {
    "extract": "formatjs extract"

Then execute the extraction with the following command:

npm run extract -- 'src/**/*.js*' --out-file src/intl/en.json --format simple

This will create a file called src/intl/en.json, the content of which will contain all of the default messages from your <FormattedMessages /> and will look something like this:

  "email_label": "Email address",
  "email_input": "Email address",
  "password_label": "Password",
  "submit_button": "submit"

With the id’s used as the key and the default message used as the value. The JSON file is now ready for you to pass to your translator!

Use your translations

When your JSON has been translated it needs to be saved in the same location as the original extracted file, and the file name must match the name of the locale. eg: src/intl/fr.json

That’s all there is to it!

Now when a user requests your site in their preferred language, Gatsby will use the strings from the corresponding JSON file (e.g.  fr.json for French) to display the translated strings. If there is no corresponding file or key within the file, the default message will be displayed.

Congratulations, you have made the first step in connecting with a global customer base!

So if internationalization is the first step, what’s next? Get in touch so that we can help you automate the whole localization process for your Saas business.