Home / Blog / i18next and react-i18next: a complete guide for React apps

i18next and react-i18next: a complete guide for React apps

Developer resources
Markus Merzinger
Senior Developer

Last updated

7/24/2026

Read time

9 min

Best for

Developers

Stylized React browser window illustration representing i18next in React for building multilingual React applications with scalable localization and translation management.

i18next is the internationalization framework most JavaScript teams reach for when an application needs to ship in more than one language, and react-i18next connects it to React through a small set of hooks and components. This guide walks through a complete setup for a real React project: installing the libraries, structuring translation files in the i18next JSON format, translating components with the useTranslation hook, handling plurals and interpolation, and switching languages at runtime. It closes with the workflow practices that keep translations manageable once translators, reviewers, and product teams get involved.

The examples use TypeScript and assume an existing React application. Everything shown here works the same way in a plain JavaScript codebase.

What is i18next?

i18next is an open source internationalization framework for JavaScript. At its core, it loads translation resources, resolves a key like checkout.title to the right string for the active language, and applies language rules such as plural forms along the way. The official i18next documentation describes it as a full ecosystem rather than a single library: plugins add language detection, resource loading, and bindings for frameworks like React.

The core library is framework-agnostic. It runs in the browser, in Node.js, and in native environments, which means the same translation files and the same key structure can serve a React frontend and a backend service. For React projects, react-i18next connects that core to your components. The two libraries split the work like this:

i18next

react-i18next

Role

Core internationalization framework

React bindings for i18next

Environment

Any JavaScript runtime

React applications

Provides

Translation engine, plurals, interpolation, plugin system

useTranslation hook, language switching, Suspense integration

Typical install

i18next plus plugins

react-i18next alongside i18next

In practice you always install both for a React project. i18next does the translating; react-i18next makes translations reactive and re-renders components when the language changes.

Installing and configuring i18next in a React project

A production i18next React setup usually adds two plugins on top of the core libraries: i18next-http-backend loads translation files on demand instead of bundling them into your JavaScript, and i18next-browser-languagedetector picks an initial language from the browser.

npm install i18next react-i18next
i18next-http-backend i18next-browser-languagedetector

Create a dedicated configuration module, for example src/i18n.ts:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import HttpBackend from "i18next-http-backend";
import LanguageDetector from "i18next-browser-languagedetector";

i18n
  .use(HttpBackend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    fallbackLng: "en",
    ns: ["common"],
    defaultNS: "common",
    interpolation: {
      escapeValue: false, // React already escapes rendered values
    },
    backend: {
      loadPath: "/locales/{{lng}}/{{ns}}.json",
    },
  });

export default i18n;

fallbackLng defines the language i18next serves when a translation is missing in the active language; users see English instead of a raw key. ns and defaultNS declare the namespaces the app uses, which we will cover in the next section. loadPath tells the backend where to fetch JSON files, with {{lng}} and {{ns}} filled in at request time.

Import the module once in your entry point, before the app renders. Since translations now load over HTTP, wrap the app in Suspense so React has something to show while the first files arrive:

import { StrictMode, Suspense } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./i18n";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <Suspense fallback={<p>Loading</p>}>
      <App />
    </Suspense>
  </StrictMode>
);

With this in place, i18next initializes on startup, detects the user’s language, and fetches the matching JSON files from your public folder.

Structuring translation files with the i18next JSON format

i18next reads translations from plain JSON files, organized by language and namespace. A namespace is simply a named file that groups related keys, and splitting by feature keeps files small enough to review and lazy-load:

public/
  locales/
    en/
      common.json
      checkout.json
    de/
      common.json
      checkout.json

The file contents follow the i18next JSON format, currently at version 4. The format looks like ordinary nested JSON, but specific key suffixes and placeholder patterns carry meaning:

{
  "header": {
    "title": "Your account",
    "greeting": "Hello, {{name}}!"
  },
  "cartItem_one": "{{count}} item in your cart",
  "cartItem_other": "{{count}} items in your cart"
}

The table below summarizes the JSON v4 capabilities you will use most:

Feature

Syntax

What it does

Nested keys

"header": { "title": "..." }

Groups keys hierarchically; accessed as header.title

Interpolation

{{name}}

Inserts runtime values into the string

Plural suffixes

_one, _other, _few, _many

Selects the correct form based on a count value

Context

_male, _female, or any custom context

Switches strings based on a context you pass

Nesting references

$t(otherKey)

Reuses another translation inside a string

Two structural decisions pay off later. First, keep every language file shaped identically: same keys, same nesting, same namespaces. Second, treat these JSON files as the single source of truth that flows between your repository and your translation tooling. LingoHub imports and exports this exact format, so a clean, consistent v4 structure means translators always work against the current state of the app.

Fallback languages deserve a brief note. fallbackLng accepts more than a single string: with fallbackLng: { "de-AT": ["de", "en"], default: ["en"] }, an Austrian German user falls back to German before English. Regional variants stay thin this way, holding only the keys that genuinely differ.

How to use react-i18next in components

With configuration and files in place, translating a component takes one hook. useTranslation returns the t function bound to the active language and re-renders the component when the language changes:

import { useTranslation } from "react-i18next";

function Header({ userName }: { userName: string }) {
  const { t } = useTranslation();

  return (
    <header>
      <h1>{t("header.title")}</h1>
      <p>{t("header.greeting", { name: userName })}</p>
    </header>
  );
}

The second argument to t supplies interpolation values: {{name}} in the JSON file receives userName at render time. To read from a specific namespace, pass its name to the hook, as in useTranslation("checkout"). The hook accepts several more options, including loading multiple namespaces and overriding key prefixes; the react-i18next useTranslation docs list the full set.

React i18next plural handling with JSON v4 suffixes

Pluralization means changing the string form depending on a number: 1 item versus 2 items. i18next resolves plurals through the Intl.PluralRules API, which knows the correct set of forms for each language. In your JSON files, you define one key per plural form using suffixes, and in code you call the base key with a count value:

{
  "cartItem_one": "{{count}} item in your cart",
  "cartItem_other": "{{count}} items in your cart"
}
t("cartItem", { count: 1 }); // "1 item in your cart"
t("cartItem", { count: 4 }); // "4 items in your cart"

English needs only _one and _other, but many languages use additional categories. Polish, for example, distinguishes _few and _many as well. The Polish translation file carries four forms of the same key:

{
  "cartItem_one": "{{count}} przedmiot w koszyku",
  "cartItem_few": "{{count}} przedmioty w koszyku",
  "cartItem_many": "{{count}} przedmiotów w koszyku",
  "cartItem_other": "{{count}} przedmiotu w koszyku"
}

Your translators add those keys in their language files; the code stays untouched, and the same t("cartItem", { count }) call picks the right form in every language. The most common mistake in react i18next plural setups is passing the number as a pre-formatted string or under a different variable name: the value must arrive as count, as a number, or i18next cannot select a form.

Language switching and detection

The browser language detector sets a sensible initial language by checking, in order, sources such as the query string, localStorage, and the browser’s language settings. For manual switching, react-i18next exposes the i18n instance from the same hook:

import { useTranslation } from "react-i18next";

const languages = [
  { code: "en", label: "English" },
  { code: "de", label: "Deutsch" },
];

function LanguageSwitcher() {
  const { i18n } = useTranslation();

  return (
    <select
      value={i18n.resolvedLanguage}
      onChange={(event) => i18n.changeLanguage(event.target.value)}
    >
      {languages.map((language) => (
        <option key={language.code} value={language.code}>
          {language.label}
        </option>
      ))}
    </select>
  );
}

changeLanguage does three things: it loads any missing namespace files for the new language, writes the choice into the detector’s cache for the next visit, and re-renders every component that uses useTranslation. Note the use of i18n.resolvedLanguage for the select value: it reflects the language actually being served, which is safer for UI state than the raw detected value.

Best practices for production i18next setups

Getting i18next running is the quick part. Keeping hundreds of keys consistent across several languages and a dozen contributors takes more care, and a few habits prevent most of the damage:

  • Name keys by meaning, not by content. checkout.submitButton survives a copy change; clickToBuyNow does not.

  • Split namespaces along feature boundaries. One namespace per major feature area keeps files reviewable and lets routes load only what they render.

  • Never build sentences by concatenating translated fragments. Word order varies across languages; keep each sentence as one key and use interpolation for the dynamic parts.

  • Turn on debug: true in development. i18next then logs every missing key to the console, where it gets noticed and fixed early.

  • Guard placeholder integrity. A translator who accidentally renames {{count}} to {{Anzahl}} breaks the string at runtime. LingoHub’s quality checks catch this class of error automatically before it reaches a release, along with missing translations and inconsistent terminology.

  • Keep all language files structurally identical. Diff-friendly, consistently ordered JSON makes both code review and machine processing reliable. LingoHub takes care of this alignment for you: every key in the source language is tracked across all target language files, so exports always share the same structure.

For TypeScript teams, react-i18next can also type-check translation keys themselves through a small type augmentation, giving autocomplete for every key and compile-time errors for typos. The react-i18next documentation describes the setup under its TypeScript section.

Common i18next mistakes

A few problems show up in almost every i18next codebase. They stay invisible in the source language and surface later, in a language nobody on the team reads daily:

Mistake

What you see

Fix

Hardcoded strings in components

English text in every language

Wrap all user-facing text in t() from the start

Leaving escapeValue on its default

Apostrophes render as &#39;

Set interpolation.escapeValue: false; React escapes on its own

No Suspense above translated components

Blank screen or render errors on first load

Add a Suspense fallback around the app

Different key structure per language

Working English, missing German strings

Keep every language file structurally identical

Reading i18n.language for UI state

Switcher shows de-AT while the app serves de

Use i18n.resolvedLanguage instead

Editing target-language files by hand

Languages drift apart over time

Route every change through one source of truth

Managing i18next translations with LingoHub

The setup above solves the code side of localization. The remaining work is operational: getting JSON files to translators, tracking what is untranslated after each feature, and verifying quality across languages. Doing this by hand, over email or shared folders, breaks down quickly once a project grows past a couple of languages.

LingoHub handles this workflow for i18next projects natively. It imports and exports i18next JSON files directly, supporting the current v4 format as well as the outdated v3 format for older projects, and it fully supports i18next plurals: suffix keys such as _one and _other are recognized and kept together, so translators see all plural forms of a string side by side. The developer documentation covers the format details and configuration options.

The workflow fits how development teams already operate. LingoHub connects to your git repository, pulls new and changed keys from public/locales automatically, and opens a pull request when translations are ready. Localization runs alongside your normal branching model instead of as a separate process. While translators and reviewers work in parallel, built-in quality checks verify that {{placeholders}} remain intact, plural suffix keys are complete, and terminology follows your glossary. AI translation can prefill new keys in seconds, with human review deciding what ships. The result for a development team is concrete: translation stops being a release blocker, and mismatched placeholders stop appearing in production.

Frequently asked questions

What is react-i18next?

react-i18next is the official React binding for i18next. It wraps the i18next core in React-friendly APIs: the useTranslation hook for accessing translations in components, language switching that re-renders the UI, and integration with Suspense for asynchronous loading. It contains no translation logic of its own; i18next remains the engine underneath.

How to get current language in i18next?

Read i18n.language for the currently detected or selected language code, which may include a region such as de-AT. For UI purposes, i18n.resolvedLanguage is usually the better choice: it returns the language i18next actually resolved translations to, after fallbacks. In a component, get the instance from the hook: const { i18n } = useTranslation().

Does react-i18next support TypeScript?

Yes. Beyond shipping its own type definitions, react-i18next supports fully typed translation keys: you augment the i18next module with the shape of your resources, and t gains autocomplete plus compile-time checking for every key and interpolation variable. For large key sets, generating that type from your English JSON files in a build step keeps it accurate.

How do I keep translation files consistent across languages?

Automate the checks. Structural consistency (every key present in every language, placeholders unchanged, plural forms complete) is exactly what machines verify better than reviewers. LingoHub runs these checks continuously on your i18next JSON files, and a CI step can additionally fail a build when a language file falls behind the source language.

Wrapping up

A production-ready i18next React setup comes down to a handful of decisions made early: load translations from namespaced JSON v4 files instead of bundling them, let a detector and fallback chain choose the language, use useTranslation for all user-facing strings, and express plurals through suffix keys rather than code. From there, the file format does the heavy lifting: clean i18next JSON moves between your repository and your translation workflow without manual copying or rework.

Once a project supports more than one or two languages, the work shifts from translating individual strings to managing the whole workflow. Automated synchronization, quality checks, and reviews keep localization moving at the pace of development. Ready to see the LingoHub i18next integration in action? Book a demo and bring your own JSON files.

Related articles