Add lazy loading to React i18next with React Suspense

Translation files can get huge, and you may have multiple languages being imported into your app. That is a problem!

This article will cover:

How do you load the only needed translation?

The bigger the application the bigger the translation files, and the more languages you will be supporting.

For example:


import translationEN from '../public/locales/en/translation.json';
import translationDE from '../public/locales/de/translation.json';
import translationAU from '../public/locales/au/translation.json';
import translationUK from '../public/locales/uk/translation.json';

// the translations
const resources = {
  en: {
    translation: translationEN,
  },
  de: {
    translation: translationDE,
  },
  au: {
    translation: translationAU,
  },
  uk: {
    translation: translationUK,
  },
}

and we will also cover an error you may come across:


Error: withI18nextTranslation(Unknown) suspended while rendering, but no fallback UI was specified.

Step 1: Install i18n-xhr-backend NPM module

First you will need to install a middleware library that will allow the app to fetch your translations through the browsers XHR.


npm install i18next-xhr-backend --save

Step 2: Update your i18n.js file

Here’s what a default 18n.js file may look like:


import translationEN from '../public/locales/en/translation.json';
import translationDE from '../public/locales/de/translation.json';
import translationAU from '../public/locales/au/translation.json';
import translationUK from '../public/locales/uk/translation.json';

// the translations
const resources = {
  en: {
    translation: translationEN,
  },
  de: {
    translation: translationDE,
  },
  au: {
    translation: translationAU,
  },
  uk: {
    translation: translationUK,
  },
};

i18n

  .init({
    resources,

    lng: "en",

    // ... other configs

  });

It looks like a mess, and it’s uploading all the translations files that will not be used.

Let’s clean it up, and use the new i18n middleware that you installed.


import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import backend from "i18next-xhr-backend";

i18n

  // The default path is "../public/locales/en/translation.json"
  .use(backend)

  // passes i18n down to react-i18next
  .use(initReactI18next)

  .init({
    lng: "en",

    // ... other configs
  });

Much cleaner, and you don’t have to import all the languages onto your React application!

Step 3: Add React Suspense to your React application

By default react-i18next withTranslation HOC or the useTranslation hook will expect you to use React Suspense.

If you don’t you’ll get an error message saying:


Error: withI18nextTranslation(Unknown) suspended while rendering, but no fallback UI was specified.

You have the option to turn it off:


i18n

  .use(backend)

  // passes i18n down to react-i18next
  .use(initReactI18next)

  .init({
    // ... other config

    react: {
      // Turn off the use of React Suspense
      useSuspense: false
    }
  });

Just pass the false boolean value to the useSuspense configuration property.

But this may lead to some very unsexy UX. I recommend leaving it on.

Let me show you how to add React Suspense to solve the error message above.


import * as React from "react";
import { render } from "react-dom";
import "./i18n";
import { withTranslation } from "react-i18next";

const App = withTranslation()(props => {
  return <h1>{props.t("greet")}</h1>;
});

render(
  <React.Suspense fallback="loading...">
    <App />
  </React.Suspense>,
  document.getElementById("root")
);

Make sure to add the React Suspense component outside the App component, not inside.

In the example above, I used the Suspense component in the ReactDOM render function.

Here’s another example how to use it with react-i18next hook method.


import * as React from "react";
import { render } from "react-dom";
import "./i18n";
import { useTranslation } from "react-i18next";

const App = () => {
  const { t } = useTranslation();

  return <h1>{t("greet")}</h1>;
};

render(
  <React.Suspense fallback="loading...">
    <App />
  </React.Suspense>,
  document.getElementById("root")
);

Happy coding!

I like to tweet about React and post helpful code snippets. Follow me there if you would like some too!