react-i18next

React bindings for the i18next internationalization ecosystem, built on hooks and a translation-aware Trans component.

Library
npm
v17.0.15
10,046 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
77 /100 Good
Development Activity 88
Maintenance 44
Community 76
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
81 /100 Excellent
Architecture 85
Code Quality 82
Innovation 80
Learning Curve 78

react-i18next is the official React integration for i18next, the most widely used JavaScript internationalization framework. It exposes a useTranslation hook, a Trans component for translating JSX with embedded markup and interpolated variables, and higher-order-component/class alternatives (withTranslation, Translation) for every React rendering style, including React Native and server-side rendering.

Rather than reinventing i18n primitives, the library is a thin, React-idiomatic layer over i18next’s mature backend, plugin, and pluralization system — so translation loading, namespace splitting, language detection, and interpolation all inherit i18next’s plugin ecosystem while React apps get first-class hooks, context, and Suspense integration.

What You Get

  • useTranslation() hook returning a t() translation function plus i18n instance and ready state, backed by useSyncExternalStore for tear-free concurrent rendering
  • Trans component for translating JSX fragments with embedded elements (links, bold text, interpolated components) without breaking out of markup
  • withTranslation HOC and Translation render-prop component for class components and non-hook codebases
  • I18nextProvider and I18nContext for supplying a scoped i18next instance via React context, useful in multi-tenant or testing scenarios
  • Built-in SSR helpers (useSSR, withSSR, getInitialProps) for hydrating translations fetched server-side without a client-side flash of untranslated content
  • ICU MessageFormat support via IcuTrans/IcuTransWithoutContext and an icu.macro babel macro for pluralization, gender, and select formatting

Common Use Cases

  • Translating a multi-language marketing site or SaaS dashboard where namespaces are split per route or feature for lazy loading
  • Localizing a React Native mobile app alongside a shared i18next configuration used by a companion web app
  • Server-rendering a Next.js or Remix page with the correct language pre-resolved to avoid translation flicker on hydration
  • Translating rich JSX content — e.g. “Accept the <TermsLink>terms</TermsLink>” — where naive string translation would break embedded links or formatting
  • Adding pluralization and ICU-based gender/number formatting to a product that already has i18next configured for the backend

Under The Hood

Architecture — react-i18next is a thin React-idiomatic wrapper around an i18next instance rather than its own translation engine. src/context.js holds a shared I18nContext (React context) plus module-level getI18n/setI18n accessors so a default instance can be set once via initReactI18next and consumed anywhere without prop drilling; I18nextProvider overrides that instance per-subtree for multi-tenant or test scenarios. useTranslation.js is the core: it derives the active namespaces, subscribes to i18next’s event emitter (bindI18n/bindI18nStore options) through useSyncExternalStore, and recomputes a memoized snapshot (t, i18n, ready) only when language, namespace-load state, or a revision counter actually changes — avoiding tearing under React 18/19 concurrent rendering. TransWithoutContext.js (656 lines, the largest module) implements JSX-aware translation by walking React children, converting them to an HTML-like string via nodesToString, looking up the translated string, then re-hydrating it back into React elements with html-parse-stringify, so markup and interpolated components survive translation. SSR support (useSSR/withSSR/getInitialProps) reads i18n.reportNamespaces (built by ReportNamespaces in context.js) to know which namespaces were actually used during a render pass, so only those get serialized into the initial payload.

Tech Stack — Pure JavaScript source (src/*.js) with hand-written .d.ts/.d.mts type declarations shipped separately rather than compiled from TypeScript, built via Rollup (rollup.config.mjs) into dual CommonJS/ESM output under dist/. Runtime dependencies are minimal and deliberate: @babel/runtime for helper functions, html-parse-stringify for the Trans component’s markup round-tripping, and use-sync-external-store (with its official shim) for React 16.8+ compatible external-store subscriptions. i18next and react/react-dom are peer dependencies (i18next >=26.2.0, react >=16.8.0), keeping the library agnostic to which i18next plugins or React version the host app uses. Tests run on Vitest with @testing-library/react and happy-dom.

Code Quality — The test suite (32 spec files under test/, including dedicated suites for Trans rendering with dynamic/ICU/object children, useTranslation, SSR, and a typescript/ folder for type-level tests) covers the library’s trickiest surface — JSX-to-string-and-back translation — thoroughly. Source files are small and single-purpose (most under 100 lines; TransWithoutContext.js and useTranslation.js are the deliberate exceptions given their complexity). Warnings use a centralized warn/warnOnce in utils.js that route through i18next’s own logger when available, giving consistent, de-duplicated console output instead of ad-hoc console.warn calls scattered through the codebase. ESLint (airbnb config) and Prettier are enforced via lint-staged/husky pre-commit hooks.

API Design - The hook API is minimal by design: useTranslation(ns) returns { t, i18n, ready }, and t() itself accepts the same key/options signature as vanilla i18next, so developers already familiar with i18next transfer knowledge directly. The Trans component’s children-as-translation-target pattern (translate the JSX tree itself, keyed by an i18nKey) is unusual compared to typical t('key', {values}) calls elsewhere in the API, and is the one part of the surface that has a real learning curve — the README addresses this with a dedicated before/after example. Class-component parity (withTranslation, Translation) and hook-based access use the same underlying options object shape, so switching between the two styles doesn’t require relearning configuration.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers