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!