React i18n: The Complete Setup Guide
Adding internationalization (i18n) to a React application is one of the most impactful things you can do for global reach. Whether you are building a SaaS product, an e-commerce platform, or a developer tool, React i18n lets you serve users in their native language — and dramatically improves conversion rates.
This guide walks you through setting up i18n in React from scratch, covering the three most popular approaches: react-i18next, react-intl, and Lovalingo.
Why React i18n Matters in 2026
The web is global by default. Here is why i18n should be a priority:
- Many internet users prefer browsing in their native language
- Localized apps see 1.5-3x higher conversion rates compared to English-only equivalents
- Google indexes localized content separately, giving you 10x the keyword surface area
- App stores rank localized apps higher in regional search results
If you are targeting any market outside of the US/UK, React i18n is not optional — it is a competitive advantage.
React i18n: Three Approaches Compared
Before diving into setup, let's understand the three main approaches to React internationalization:
| Approach | Library | How It Works | Effort |
|----------|---------|-------------|--------|
| Manual JSON | react-i18next | Extract strings → create JSON files → use t() function | High |
| ICU Messages | react-intl | Define messages with ICU format → use FormattedMessage | High |
| Automatic AI | Lovalingo | Wrap app in provider → translations happen automatically | Minimal |
When to Choose Each
- react-i18next: Best for large teams with dedicated translators and complex namespacing needs
- react-intl: Best when you need advanced ICU message formatting (plurals, selects, dates)
- Lovalingo: Best for fast-moving teams who want i18n without the overhead of translation files
Setting Up React i18n with react-i18next
react-i18next is the most popular React i18n library with over 9 million weekly npm downloads. Here is the complete setup:
Step 1: Install Packages
npm install i18next react-i18next i18next-browser-languagedetector i18next-http-backendEach package serves a purpose:
i18next— core i18n frameworkreact-i18next— React bindings (hooks, components, HOCs)i18next-browser-languagedetector— auto-detects user languagei18next-http-backend— loads translation files asynchronously
Step 2: Create Your Translation Files
Organize translations by language and namespace:
public/
locales/
en/
common.json
home.json
fr/
common.json
home.json
de/
common.json
home.json
public/locales/en/common.json:
{
"nav": {
"home": "Home",
"pricing": "Pricing",
"docs": "Documentation"
},
"cta": {
"getStarted": "Get Started Free",
"learnMore": "Learn More"
}
}public/locales/fr/common.json:
{
"nav": {
"home": "Accueil",
"pricing": "Tarifs",
"docs": "Documentation"
},
"cta": {
"getStarted": "Commencer Gratuitement",
"learnMore": "En Savoir Plus"
}
}Step 3: Initialize i18next
Create a configuration file that ties everything together:
// src/i18n.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpBackend from "i18next-http-backend";
i18n
.use(HttpBackend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: "en",
supportedLngs: ["en", "fr", "de", "es", "ja"],
defaultNS: "common",
ns: ["common", "home"],
backend: {
loadPath: "/locales/{{lng}}/{{ns}}.json",
},
detection: {
order: ["path", "cookie", "navigator"],
lookupFromPathIndex: 0,
},
interpolation: {
escapeValue: false,
},
});
export default i18n;Step 4: Wire It Into Your App
// src/main.tsx
import React, { Suspense } from "react";
import ReactDOM from "react-dom/client";
import "./i18n"; // Initialize i18n before app renders
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<Suspense fallback={<div>Loading translations...</div>}>
<App />
</Suspense>
</React.StrictMode>
);Step 5: Use Translations in Components
import { useTranslation } from "react-i18next";
function Navbar() {
const { t, i18n } = useTranslation();
return (
<nav>
<a href="/">{t("nav.home")}</a>
<a href="/pricing">{t("nav.pricing")}</a>
<a href="/docs">{t("nav.docs")}</a>
<select
value={i18n.language}
onChange={(e) => i18n.changeLanguage(e.target.value)}
>
<option value="en">English</option>
<option value="fr">Francais</option>
<option value="de">Deutsch</option>
</select>
</nav>
);
}Step 6: Handle Pluralization and Interpolation
{
"items_one": "{{count}} item in your cart",
"items_other": "{{count}} items in your cart",
"welcome": "Welcome back, {{name}}!",
"lastLogin": "Last login: {{date, datetime}}"
}function CartSummary({ count, userName }: { count: number; userName: string }) {
const { t } = useTranslation();
return (
<div>
<p>{t("welcome", { name: userName })}</p>
<p>{t("items", { count })}</p>
</div>
);
}Setting Up React i18n with react-intl
react-intl uses the ICU Message Format standard, which is more expressive for complex pluralization and selection patterns.
Quick Setup
npm install react-intlimport { IntlProvider, FormattedMessage, useIntl } from "react-intl";
const messages = {
en: {
greeting: "Hello, {name}!",
items: "{count, plural, =0 {No items} one {# item} other {# items}}",
price: "Price: {amount, number, ::currency/USD}",
},
fr: {
greeting: "Bonjour, {name} !",
items: "{count, plural, =0 {Aucun article} one {# article} other {# articles}}",
price: "Prix : {amount, number, ::currency/EUR}",
},
};
function App() {
const locale = "fr";
return (
<IntlProvider locale={locale} messages={messages[locale]}>
<ProductPage />
</IntlProvider>
);
}
function ProductPage() {
const intl = useIntl();
return (
<div>
<h1>
<FormattedMessage id="greeting" values={{ name: "Alice" }} />
</h1>
<p>
<FormattedMessage id="items" values={{ count: 3 }} />
</p>
<p>{intl.formatNumber(29.99, { style: "currency", currency: "USD" })}</p>
</div>
);
}react-intl vs react-i18next: Key Differences
| Feature | react-intl | react-i18next |
|---------|-----------|---------------|
| Message format | ICU standard | Custom syntax |
| Pluralization | Native ICU plural rules | Suffix-based (_one, _other) |
| Date/number formatting | Built-in via Intl API | Requires plugins |
| Namespace support | No (flat messages) | Yes (multi-file) |
| Lazy loading | Manual | Built-in with backends |
| Community size | Large | Largest |
The Managed Browser-Runtime Approach: React i18n with Lovalingo
react-i18next and react-intl make the application own message catalogs and keys. Lovalingo moves rendered-text translation into a hosted browser runtime and managed bundles, removing hand-maintained locale dictionaries while introducing a runtime dependency that must be tested.
Standard React setup
npm install @lovalingo/lovalingoimport { LovalingoProvider } from "@lovalingo/lovalingo/core";
function App() {
return (
<LovalingoProvider
publicAnonKey="your-public-key"
defaultLocale="en"
locales={["en", "fr", "de", "es", "ja"]}
>
{/* Your app; add exclusions and formatting controls as needed */}
<Navbar />
<HeroSection />
<PricingTable />
<Footer />
</LovalingoProvider>
);
}That is the standard client-rendered React setup. In TanStack Start or Next.js, mount the provider in client code and leave server-rendered locale routes and SEO with the host framework. There are no hand-maintained locale dictionaries or extraction step. The runtime and service:
- Detects translatable text after it is rendered in the browser
- Translates content using context-aware AI (not word-by-word)
- Updates rendered browser DOM when managed translations are available
- Caches translations on the CDN; measure warm, cold, slow-network, and failure behavior
Why Developers Choose Lovalingo for React i18n
- Zero translation files — No JSON files to create or maintain
- Runtime miss handling — New rendered text is detected and reported; verify generation and publication latency
- Loading controls — Cached bundles and an overlay aim to reduce flash; verify layout shift and hydration on the target app
- SEO integration — Runtime translation plus host-rendered locale content, metadata, canonical, hreflang, and sitemap checks on SSR stacks
- Works in browser-rendered React output — verify the client boundary and host responsibilities for Vite, Next.js, Remix, Lovable, v0, and Bolt
React i18n with Next.js App Router
If you are using Next.js 14+ with the App Router, i18n setup has some specific considerations:
Middleware-Based Locale Routing
// middleware.ts
import { NextRequest, NextResponse } from "next/server";
const locales = ["en", "fr", "de", "es"];
const defaultLocale = "en";
function getLocale(request: NextRequest): string {
const acceptLang = request.headers.get("accept-language");
if (acceptLang) {
const preferred = acceptLang.split(",")[0].split("-")[0];
if (locales.includes(preferred)) return preferred;
}
return defaultLocale;
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const hasLocale = locales.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
);
if (hasLocale) return;
const locale = getLocale(request);
request.nextUrl.pathname = `/${locale}${pathname}`;
return NextResponse.redirect(request.nextUrl);
}
export const config = {
matcher: ["/((?!api|_next|.*\\..*).*)"],
};Server Components with i18n
// app/[locale]/page.tsx
import { getDictionary } from "@/lib/dictionaries";
export default async function HomePage({
params: { locale },
}: {
params: { locale: string };
}) {
const dict = await getDictionary(locale);
return (
<main>
<h1>{dict.home.title}</h1>
<p>{dict.home.description}</p>
</main>
);
}Add Lovalingo as a Client Runtime
In Next.js, do not statically import the provider into the server layout. Put the @lovalingo/lovalingo/core dynamic import in a client provider that mounts after hydration, wrap it in an error boundary, and keep a source-language fallback. Let Next.js generate raw locale routes, html[lang], metadata, canonical tags, hreflang, and sitemap entries. Use seo={false} when Next.js owns the head.
React i18n Best Practices
1. Plan for Text Expansion
German and French text is typically 20-35% longer than English. Always:
- Use flexible layouts (flexbox, grid) instead of fixed widths
- Test with pseudo-localization to simulate longer strings
- Avoid truncating translated text — it can change meaning
2. Use Context for Translations
A word like "post" can mean different things. Provide context to translators:
{
"blog.post": "Post",
"social.post": "Publish",
"mail.post": "Send"
}3. Never Concatenate Translated Strings
Word order varies dramatically across languages:
// Bad — assumes English word order
const msg = t("hello") + " " + name + ", " + t("welcome");
// Good — let the translator control word order
const msg = t("greeting", { name });
// EN: "Hello Alice, welcome!"
// JA: "アリスさん、ようこそ!"4. Handle RTL Languages
Support Arabic, Hebrew, and Persian with logical CSS properties:
/* Instead of margin-left, use margin-inline-start */
.sidebar {
margin-inline-start: 1rem;
padding-inline-end: 1rem;
}5. Extract Early, Extract Often
The longer you wait to add i18n, the harder it becomes. Start with i18n on day one — or use Lovalingo to avoid extraction entirely.
6. Test with Real Languages
Do not rely solely on pseudo-localization. Test with actual translations in languages that:
- Have longer text (German)
- Use different scripts (Japanese, Arabic)
- Have complex pluralization (Polish, Arabic)
- Read right-to-left (Arabic, Hebrew)
Performance Optimization for React i18n
Lazy Load Translations
With react-i18next, load translations on demand:
i18n.init({
partialBundledLanguages: true,
resources: {
en: { common: require("./locales/en/common.json") },
},
backend: {
loadPath: "/locales/{{lng}}/{{ns}}.json",
},
});Use Namespaces to Split Bundles
// Only loads the "dashboard" namespace
const { t } = useTranslation("dashboard");Lovalingo: No Dictionaries in the App Bundle
Lovalingo fetches managed translations from a CDN instead of packaging locale dictionaries with application code. The app still ships the Lovalingo runtime and downloads locale data, so measure total transferred bytes, caching, and failure behavior for all 10+ languages.
FAQ
What is the easiest way to add i18n to a React app?
Lovalingo is a managed browser-runtime option when you want to avoid translation files. Standard React apps mount LovalingoProvider; SSR frameworks must keep crawlable locale routes and SEO in the host app. For catalog-based approaches, react-i18next is a popular choice.
Do I need to extract all strings for React i18n?
With traditional libraries like react-i18next or react-intl, yes — you need to extract every user-facing string into JSON translation files. With Lovalingo, no extraction is needed because AI detects and translates text automatically.
Which React i18n library has the smallest bundle size?
Bundle size depends on the exact version, imports, plugins, messages, and bundler. Measure the production build you will ship. Lovalingo adds a browser runtime and downloads managed locale bundles from its CDN.
Can I use React i18n with TypeScript?
Yes, all major React i18n libraries support TypeScript. react-i18next offers typed translation keys via module augmentation. react-intl provides typed message descriptors. Lovalingo is fully typed out of the box with no additional configuration.
How do I handle dynamic content in React i18n?
Use interpolation syntax: react-i18next uses double curly braces like {{name}}, react-intl uses ICU format like {name}, and Lovalingo handles dynamic content automatically since it translates at the render level.
Ready to add i18n without message catalogs? Try Lovalingo free — use an agent-assisted browser-runtime setup and verify it on your stack.
Related Guides
React i18n Setup Guide & Best Practices
Learn how to implement React internationalization (i18n) with best practices, code examples, and Lovalingo automation. Step-by-step guide.
Read guideNext.js i18n Setup Guide
Complete guide to Next.js internationalization. Learn i18n routing, setup, and best practices. Compare next-intl, next-i18next, and Lovalingo.
Read guideBest React Translation Libraries Compared
Compare React translation libraries: react-intl, react-i18next, and Lovalingo. Features, bundle size, ease of use, and performance.
Read guideReady to automate your i18n workflow?
Lovalingo adds managed browser translation; your framework remains responsible for crawlable locale routes and SEO output.
Try Lovalingo Free