IntlPull
Guide
18 min read

react-i18next: La guía completa para la internacionalización de React en 2026

Domina react-i18next para aplicaciones React. Tutorial completo que cubre configuración, hooks, namespaces, pluralización, lazy loading, TypeScript y mejores prácticas de producción.

IntlPull Team
IntlPull Team
03 Feb 2026, 11:44 AM [PST]
On this page
Summary

Domina react-i18next para aplicaciones React. Tutorial completo que cubre configuración, hooks, namespaces, pluralización, lazy loading, TypeScript y mejores prácticas de producción.

Respuesta rápida

react-i18next es la librería i18n más popular para React con 3.5M+ descargas npm semanales. Instalar con npm install react-i18next i18next, create JSON translation files, configure i18next with i18n.init(), then use the useTranslation hook: const { t } = useTranslation(); return <h1>{t('welcome')}</h1>. For production apps, combine with IntlPull for AI-powered translation management and OTA updates.


What is react-i18next?

react-i18next is a React binding for i18next, the most battle-tested JavaScript internationalization framework. It provides:

  • React hooks (useTranslation) for functional components
  • HOCs and render props for class components
  • Suspense support for async translation loading
  • SSR compatibility with Next.js, Remix, and Gatsby
  • Plugin ecosystem for backends, language detection, and caching

Why Choose react-i18next?

Featurereact-i18nextreact-intlnext-intl
Weekly Downloads3.5M+1.2M800K
Bundle Size~15KB~25KB~12KB
FrameworkAny ReactAny ReactNext.js only
Learning CurveLowMediumLow
Plugin SystemExtensiveLimitedN/A
TypeScriptExcellentGoodExcellent

Installation and Setup

Step 1: Install Dependencies

Terminal
1npm install react-i18next i18next
2# or
3yarn add react-i18next i18next
4# or
5pnpm add react-i18next i18next

Optional plugins:

Terminal
1# Language detection
2npm install i18next-browser-languagedetector
3
4# Load translations from backend
5npm install i18next-http-backend
6
7# For React Native
8npm install react-i18next i18next @os-team/i18next-react-native-language-detector

Step 2: Create Translation Files

src/
├── locales/
│   ├── en/
│   │   ├── common.json
│   │   └── home.json
│   ├── es/
│   │   ├── common.json
│   │   └── home.json
│   └── fr/
│       ├── common.json
│       └── home.json
└── i18n.ts

en/common.json:

JSON
1{
2  "welcome": "Welcome to our app",
3  "nav": {
4    "home": "Home",
5    "about": "About",
6    "contact": "Contact"
7  },
8  "buttons": {
9    "submit": "Submit",
10    "cancel": "Cancel",
11    "save": "Save"
12  }
13}

es/common.json:

JSON
1{
2  "welcome": "Bienvenido a nuestra aplicación",
3  "nav": {
4    "home": "Inicio",
5    "about": "Acerca de",
6    "contact": "Contacto"
7  },
8  "buttons": {
9    "submit": "Enviar",
10    "cancel": "Cancelar",
11    "save": "Guardar"
12  }
13}

Step 3: Configure i18next

TypeScript
1// src/i18n.ts
2import i18n from 'i18next';
3import { initReactI18next } from 'react-i18next';
4import LanguageDetector from 'i18next-browser-languagedetector';
5
6// Import translations
7import enCommon from './locales/en/common.json';
8import enHome from './locales/en/home.json';
9import esCommon from './locales/es/common.json';
10import esHome from './locales/es/home.json';
11
12i18n
13  .use(LanguageDetector)
14  .use(initReactI18next)
15  .init({
16    resources: {
17      en: {
18        common: enCommon,
19        home: enHome,
20      },
21      es: {
22        common: esCommon,
23        home: esHome,
24      },
25    },
26    defaultNS: 'common',
27    fallbackLng: 'en',
28    supportedLngs: ['en', 'es', 'fr'],
29
30    interpolation: {
31      escapeValue: false, // React already escapes
32    },
33
34    detection: {
35      order: ['localStorage', 'navigator', 'htmlTag'],
36      caches: ['localStorage'],
37    },
38  });
39
40export default i18n;

Step 4: Initialize in Your App

TSX
1// src/main.tsx or src/index.tsx
2import React from 'react';
3import ReactDOM from 'react-dom/client';
4import './i18n'; // Import i18n configuration
5import App from './App';
6
7ReactDOM.createRoot(document.getElementById('root')!).render(
8  <React.StrictMode>
9    <App />
10  </React.StrictMode>
11);

Using the useTranslation Hook

The useTranslation hook is the primary way to access translations in functional components.

Basic Usage

TSX
1import { useTranslation } from 'react-i18next';
2
3function Header() {
4  const { t } = useTranslation();
5
6  return (
7    <header>
8      <h1>{t('welcome')}</h1>
9      <nav>
10        <a href="/">{t('nav.home')}</a>
11        <a href="/about">{t('nav.about')}</a>
12      </nav>
13    </header>
14  );
15}

With Namespaces

TSX
1import { useTranslation } from 'react-i18next';
2
3function HomePage() {
4  // Load specific namespace
5  const { t } = useTranslation('home');
6
7  return <h1>{t('hero.title')}</h1>;
8}
9
10function MultiNamespace() {
11  // Load multiple namespaces
12  const { t } = useTranslation(['common', 'home']);
13
14  return (
15    <div>
16      <h1>{t('home:hero.title')}</h1>
17      <button>{t('common:buttons.submit')}</button>
18    </div>
19  );
20}

Changing Language

TSX
1import { useTranslation } from 'react-i18next';
2
3function LanguageSwitcher() {
4  const { i18n } = useTranslation();
5
6  const changeLanguage = (lng: string) => {
7    i18n.changeLanguage(lng);
8  };
9
10  return (
11    <div>
12      <button onClick={() => changeLanguage('en')}>English</button>
13      <button onClick={() => changeLanguage('es')}>Español</button>
14      <button onClick={() => changeLanguage('fr')}>Français</button>
15    </div>
16  );
17}

Variable Interpolation

Basic Variables

JSON
1{
2  "greeting": "Hello, {{name}}!",
3  "itemCount": "You have {{count}} items in your cart"
4}
TSX
1function Greeting({ user }) {
2  const { t } = useTranslation();
3
4  return (
5    <div>
6      <h1>{t('greeting', { name: user.name })}</h1>
7      <p>{t('itemCount', { count: user.cartItems })}</p>
8    </div>
9  );
10}

Formatting Values

JSON
1{
2  "price": "Total: {{price, currency}}",
3  "date": "Created on {{date, datetime}}"
4}
TypeScript
1// i18n.ts - Add formatters
2i18n.init({
3  // ... other config
4  interpolation: {
5    escapeValue: false,
6    format: (value, format, lng) => {
7      if (format === 'currency') {
8        return new Intl.NumberFormat(lng, {
9          style: 'currency',
10          currency: 'USD',
11        }).format(value);
12      }
13      if (format === 'datetime') {
14        return new Intl.DateTimeFormat(lng).format(value);
15      }
16      return value;
17    },
18  },
19});

Pluralization

Basic Plurals

JSON
1{
2  "item_one": "{{count}} item",
3  "item_other": "{{count}} items",
4  "item_zero": "No items"
5}
TSX
1function CartCount({ count }) {
2  const { t } = useTranslation();
3
4  return <span>{t('item', { count })}</span>;
5  // count=0: "No items"
6  // count=1: "1 item"
7  // count=5: "5 items"
8}

Complex Plurals (Russian, Arabic, etc.)

JSON
1{
2  "item_zero": "нет товаров",
3  "item_one": "{{count}} товар",
4  "item_few": "{{count}} товара",
5  "item_many": "{{count}} товаров",
6  "item_other": "{{count}} товаров"
7}

Ordinals

JSON
1{
2  "place_ordinal_one": "{{count}}st place",
3  "place_ordinal_two": "{{count}}nd place",
4  "place_ordinal_few": "{{count}}rd place",
5  "place_ordinal_other": "{{count}}th place"
6}
TSX
1t('place', { count: 1, ordinal: true }); // "1st place"
2t('place', { count: 2, ordinal: true }); // "2nd place"
3t('place', { count: 3, ordinal: true }); // "3rd place"
4t('place', { count: 4, ordinal: true }); // "4th place"

Rich Text and Components

Using the Trans Component

JSON
1{
2  "terms": "By signing up, you agree to our <link>Terms of Service</link>",
3  "welcome": "Welcome <bold>{{name}}</bold> to our platform!"
4}
TSX
1import { Trans, useTranslation } from 'react-i18next';
2
3function TermsText() {
4  return (
5    <Trans
6      i18nKey="terms"
7      components={{
8        link: <a href="/terms" className="text-blue-600" />,
9      }}
10    />
11  );
12}
13
14function WelcomeMessage({ name }) {
15  return (
16    <Trans
17      i18nKey="welcome"
18      values={{ name }}
19      components={{
20        bold: <strong className="font-bold" />,
21      }}
22    />
23  );
24}

Nested HTML

JSON
{
  "description": "Check out our <link>new features</link> and <button>get started</button> today!"
}
TSX
1<Trans
2  i18nKey="description"
3  components={{
4    link: <a href="/features" />,
5    button: <button onClick={handleStart} />,
6  }}
7/>

Lazy Loading Translations

For large apps, load translations on demand to reduce initial bundle size.

HTTP Backend

TypeScript
1import i18n from 'i18next';
2import { initReactI18next } from 'react-i18next';
3import HttpBackend from 'i18next-http-backend';
4import LanguageDetector from 'i18next-browser-languagedetector';
5
6i18n
7  .use(HttpBackend)
8  .use(LanguageDetector)
9  .use(initReactI18next)
10  .init({
11    fallbackLng: 'en',
12    ns: ['common'],
13    defaultNS: 'common',
14
15    backend: {
16      loadPath: '/locales/{{lng}}/{{ns}}.json',
17    },
18
19    react: {
20      useSuspense: true,
21    },
22  });
23
24export default i18n;

With Suspense

TSX
1import { Suspense } from 'react';
2
3function App() {
4  return (
5    <Suspense fallback={<LoadingSpinner />}>
6      <MainContent />
7    </Suspense>
8  );
9}

Load Namespace on Demand

TSX
1import { useTranslation } from 'react-i18next';
2import { Suspense, lazy } from 'react';
3
4// Lazy load component with its namespace
5const SettingsPage = lazy(() => import('./SettingsPage'));
6
7function App() {
8  return (
9    <Suspense fallback={<LoadingSpinner />}>
10      <SettingsPage />
11    </Suspense>
12  );
13}
14
15// SettingsPage.tsx
16function SettingsPage() {
17  const { t, ready } = useTranslation('settings', { useSuspense: false });
18
19  if (!ready) return <LoadingSpinner />;
20
21  return <h1>{t('title')}</h1>;
22}

TypeScript Integration

Type-Safe Translations

TypeScript
1// src/types/i18next.d.ts
2import 'i18next';
3import common from '../locales/en/common.json';
4import home from '../locales/en/home.json';
5
6declare module 'i18next' {
7  interface CustomTypeOptions {
8    defaultNS: 'common';
9    resources: {
10      common: typeof common;
11      home: typeof home;
12    };
13  }
14}

Now TypeScript will autocomplete translation keys:

TSX
const { t } = useTranslation();
t('nav.home'); // ✅ Autocompletes
t('nav.invalid'); // ❌ TypeScript error

With Multiple Namespaces

TSX
1const { t } = useTranslation(['common', 'home']);
2
3t('buttons.submit'); // From common (default)
4t('home:hero.title'); // From home namespace

Server-Side Rendering (SSR)

Next.js Pages Router

TypeScript
1// next-i18next.config.js
2module.exports = {
3  i18n: {
4    defaultLocale: 'en',
5    locales: ['en', 'es', 'fr'],
6  },
7};
TSX
1// pages/index.tsx
2import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
3import { useTranslation } from 'next-i18next';
4
5export default function Home() {
6  const { t } = useTranslation('common');
7  return <h1>{t('welcome')}</h1>;
8}
9
10export async function getStaticProps({ locale }) {
11  return {
12    props: {
13      ...(await serverSideTranslations(locale, ['common'])),
14    },
15  };
16}

Next.js App Router

For Next.js App Router, we recommend using next-intl instead. See our next-intl guide.

Remix

TSX
1// app/entry.server.tsx
2import { createInstance } from 'i18next';
3import { initReactI18next } from 'react-i18next';
4
5export default async function handleRequest(request, ...) {
6  const i18n = createInstance();
7  await i18n.use(initReactI18next).init({
8    lng: locale,
9    resources: { [locale]: translations },
10  });
11
12  // Render with i18n instance
13}

Testing with react-i18next

Mock Setup

TypeScript
1// test/setup.ts
2import i18n from 'i18next';
3import { initReactI18next } from 'react-i18next';
4
5i18n.use(initReactI18next).init({
6  lng: 'en',
7  fallbackLng: 'en',
8  resources: {
9    en: {
10      common: {
11        welcome: 'Welcome',
12        'buttons.submit': 'Submit',
13      },
14    },
15  },
16});
17
18export default i18n;

Testing Components

TSX
1import { render, screen } from '@testing-library/react';
2import { I18nextProvider } from 'react-i18next';
3import i18n from '../test/setup';
4import Header from './Header';
5
6describe('Header', () => {
7  it('renders translated welcome message', () => {
8    render(
9      <I18nextProvider i18n={i18n}>
10        <Header />
11      </I18nextProvider>
12    );
13
14    expect(screen.getByText('Welcome')).toBeInTheDocument();
15  });
16});

Production Best Practices

1. Namespace Organization

locales/
├── en/
│   ├── common.json      # Shared UI (buttons, nav, errors)
│   ├── auth.json        # Login, signup, password reset
│   ├── dashboard.json   # Dashboard-specific
│   ├── settings.json    # Settings page
│   └── errors.json      # Error messages

2. Key Naming Convention

JSON
1{
2  "page.section.element": "Value",
3  "auth.login.title": "Sign In",
4  "auth.login.button": "Continue",
5  "dashboard.stats.totalUsers": "Total Users",
6  "errors.network.timeout": "Request timed out"
7}

3. Missing Key Handling

TypeScript
1i18n.init({
2  saveMissing: process.env.NODE_ENV === 'development',
3  missingKeyHandler: (lng, ns, key) => {
4    console.warn(`Missing translation: ${lng}/${ns}/${key}`);
5    // Report to error tracking
6  },
7});

4. Performance Optimization

TypeScript
1// Preload critical namespaces
2i18n.loadNamespaces(['common', 'auth']);
3
4// Don't re-render on language change for static content
5const { t } = useTranslation('common', { useSuspense: false });

Scaling with IntlPull

As your app grows, managing JSON files manually becomes painful. IntlPull provides:

CLI Integration

Terminal
1# Initialize IntlPull in your project
2npx @intlpullhq/cli init
3
4# Extract keys from code
5npx @intlpullhq/cli extract
6
7# Upload to IntlPull
8npx @intlpullhq/cli upload
9
10# Download translations
11npx @intlpullhq/cli download

AI Translation

IntlPull automatically translates your keys using GPT-4, Claude, or DeepL while maintaining context and brand voice.

OTA Updates

Update translations without app releases:

TypeScript
1import { IntlPullBackend } from '@intlpull/i18next-backend';
2
3i18n.use(IntlPullBackend).init({
4  backend: {
5    projectId: 'your-project-id',
6    apiKey: process.env.INTLPULL_API_KEY,
7  },
8});

Common Patterns

Conditional Translations

JSON
1{
2  "greeting_morning": "Good morning!",
3  "greeting_afternoon": "Good afternoon!",
4  "greeting_evening": "Good evening!"
5}
TSX
1function Greeting() {
2  const { t } = useTranslation();
3  const hour = new Date().getHours();
4
5  const getGreetingKey = () => {
6    if (hour < 12) return 'greeting_morning';
7    if (hour < 18) return 'greeting_afternoon';
8    return 'greeting_evening';
9  };
10
11  return <h1>{t(getGreetingKey())}</h1>;
12}

Dynamic Keys

TSX
1function StatusBadge({ status }: { status: 'pending' | 'approved' | 'rejected' }) {
2  const { t } = useTranslation();
3
4  // Keys: status.pending, status.approved, status.rejected
5  return <span>{t(`status.${status}`)}</span>;
6}

Fallback Content

TSX
1const { t } = useTranslation();
2
3// Provide fallback
4const title = t('page.title', 'Default Title');
5
6// Check if key exists
7const { exists } = useTranslation();
8if (exists('optional.feature')) {
9  // Show feature-specific content
10}

Troubleshooting

"Translation not found" warnings

TypeScript
1// Check namespace is loaded
2i18n.hasLoadedNamespace('myNamespace');
3
4// Ensure key exists
5i18n.exists('myKey');
6
7// Debug mode
8i18n.init({ debug: true });

Component not re-rendering on language change

TSX
1// Use the hook properly - it subscribes to changes
2const { t, i18n } = useTranslation();
3
4// Force re-render with key
5<MyComponent key={i18n.language} />

SSR hydration mismatch

TypeScript
1i18n.init({
2  react: {
3    useSuspense: false, // Disable for SSR
4  },
5});

Frequently Asked Questions

What is react-i18next?

react-i18next is the official React binding for i18next, the most popular JavaScript internationalization framework. It provides hooks (useTranslation), HOCs, and the Trans component for integrating translations into React components. With 3.5M+ weekly npm downloads, it's the most widely adopted React i18n solution.

How do I install react-i18next?

Install with npm: npm install react-i18next i18next. Optionally add i18next-browser-languagedetector for auto language detection and i18next-http-backend for lazy loading translations. Create translation JSON files, configure i18next with i18n.init(), and import the config in your app entry point.

How does the useTranslation hook work?

useTranslation returns { t, i18n } where t is the translation function and i18n is the i18next instance. Call t('key') to get translations. Specify namespaces with useTranslation('namespace') or useTranslation(['ns1', 'ns2']). The hook automatically re-renders when language changes.

How do I handle pluralization in react-i18next?

Use plural suffixes in your JSON keys: item_one, item_other, item_zero. Pass count to the t function: t('item', { count: 5 }). For complex plurals (Russian, Arabic), use _zero, _one, _two, _few, _many, _other suffixes. i18next automatically selects the correct form based on the count and locale.

What is the Trans component used for?

Trans renders translations containing HTML or React components. Instead of dangerouslySetInnerHTML, use Trans: <Trans i18nKey="terms" components={{ link: <a href="/terms" /> }} /> renders "Accept our <a>Terms</a>" with the link clickable. It's essential for translations with bold text, links, or interactive elements.

How do I lazy load translations?

Use i18next-http-backend to load translations on demand. Configure backend: { loadPath: '/locales/{{lng}}/{{ns}}.json' } and enable Suspense. Translations are fetched when needed, reducing initial bundle size. Load specific namespaces with i18n.loadNamespaces(['settings']) before rendering pages that need them.

Is react-i18next compatible with TypeScript?

Yes, react-i18next has excellent TypeScript support. Create a i18next.d.ts file declaring your resources type, and you get autocomplete and type checking for translation keys. TypeScript will error on invalid keys, preventing typos and missing translations at compile time.

How do I use react-i18next with Next.js?

For Pages Router, use next-i18next with serverSideTranslations in getStaticProps/getServerSideProps. For App Router, we recommend next-intl instead as it's designed for Server Components. react-i18next can work with App Router but requires additional setup for RSC.

How do I test components using react-i18next?

Wrap components with I18nextProvider in tests using a mock i18n instance. Create a test-specific i18n config with simplified translations. Use Testing Library to verify translated text appears correctly. You can also mock the useTranslation hook directly for unit tests.

Should I use react-i18next or next-intl?

Use react-i18next for non-Next.js React apps or Next.js Pages Router projects. Use next-intl for Next.js App Router projects—it's purpose-built for Server Components and has tighter Next.js integration. Both are production-ready; the choice depends on your framework.

Summary

react-i18next is the industry standard for React internationalization:

AspectDetails
Installationnpm install react-i18next i18next
Primary HookuseTranslation()
NamespacesFeature-based organization
Plurals_one, _other suffixes
Rich TextTrans component
TypeScriptFull type safety available
SSRWorks with Next.js, Remix, Gatsby
Bundle~15KB gzipped

For production apps at scale, combine react-i18next with IntlPull for AI-powered translation management, OTA updates, and team collaboration.

Ready to streamline your i18n workflow? Start free with IntlPull — sync translations automatically with your react-i18next setup.

Tags
react-i18next
i18next
react
i18n
internationalization
tutorial
2026
IntlPull Team
IntlPull Team
Engineering

Building tools to help teams ship products globally. Follow us for more insights on localization and i18n.