React i18n setup is the process of adding internationalization to a React app using react-i18next. This guide covers installing the required packages, configuring i18next with language detection, organizing translation files by namespace, using the useTranslation hook and Trans component, handling plurals and interpolation, and automating translations with a localization platform like Crowdin.
Adding internationalization to a React app for the first time means hours of figuring out the right setup. The React i18n ecosystem has several moving parts, and most tutorials skip the practical details you actually need - the kind of React i18n setup example that mirrors a real production codebase rather than a toy I18n react example.
This guide walks through everything: setting up react-i18next in a React i18n Vite project, organizing translation files properly, wiring i18next-http-backend for lazy-loading namespaces, and - the best part - automating the entire translation workflow so you’re not manually managing JSON files for every language.
Why React Apps Need i18n
When exploring react i18n setup, consider the following.
Consider a SaaS product that initially only supports English. According to CSA Research’s localization survey, 76% of online shoppers prefer to buy products in their native language. When the team decides to expand to European markets, the choice is clear: build separate apps for each language (terrible idea) or implement proper internationalization.
Here’s what proper i18n gives you:
- Market expansion: Tap into non-English speaking markets without rebuilding your app
- User experience: Users prefer apps in their native language - conversion rates prove it
- SEO benefits: Localized content ranks better in regional search results
- Maintainability: One codebase serves all languages
The trick is setting it up right from the start. Let me show you how.
Setting Up react-i18next
React-i18next is the standard internationalization framework for React. It’s built on i18next, which means you get a mature, well-tested solution.

Installation
Start by installing the required packages:
npm install react-i18next i18next i18next-browser-languagedetector
Here’s what each package does:
- react-i18next: React bindings for i18next
- i18next: Core internationalization framework
- i18next-browser-languagedetector: Automatically detects user’s language from browser settings
Basic Configuration
Create a new file src/i18n/config.js:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
// Import your translation files
import translationEN from './locales/en/translation.json';
import translationES from './locales/es/translation.json';
import translationFR from './locales/fr/translation.json';
const resources = {
en: {
translation: translationEN
},
es: {
translation: translationES
},
fr: {
translation: translationFR
}
};
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: 'en',
debug: false,
interpolation: {
escapeValue: false // React already handles XSS protection
}
});
export default i18n;
Then import this config in your src/index.js or src/main.jsx:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './i18n/config'; // Import BEFORE your App component
import App from './App';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
How Should You Organize Translation Files?
This is where most developers make mistakes. Translation files frequently turn into unmaintainable messes with hundreds of flat keys.
Folder Structure
Here is a recommended structure:
src/
i18n/
config.js
locales/
en/
translation.json
common.json
dashboard.json
es/
translation.json
common.json
dashboard.json
fr/
translation.json
common.json
dashboard.json
Organizing Keys by Namespace
Instead of flat structures, organize keys by feature or component:
// src/i18n/locales/en/translation.json
{
"nav": {
"home": "Home",
"about": "About",
"contact": "Contact"
},
"auth": {
"login": "Log In",
"logout": "Log Out",
"signup": "Sign Up",
"password": "Password",
"forgotPassword": "Forgot your password?"
},
"dashboard": {
"welcome": "Welcome back, {{name}}!",
"stats": {
"users": "Total Users",
"revenue": "Revenue",
"growth": "Growth"
}
},
"errors": {
"required": "This field is required",
"invalidEmail": "Please enter a valid email",
"networkError": "Network error. Please try again."
}
}
This nested structure makes it easy to find keys later. When working on the dashboard, all dashboard-related translations live under dashboard.*.
Using Translations in Components
React-i18next gives you several ways to access translations - the i18next API documentation covers each helper in detail. Here are the most useful patterns:
useTranslation Hook (Recommended)
This is the recommended method for functional components, mirroring the pattern documented in the official useTranslation hook reference:
import { useTranslation } from 'react-i18next';
function LoginForm() {
const { t } = useTranslation();
return (
<form>
<h2>{t('auth.login')}</h2>
<input
type="email"
placeholder={t('auth.email')}
/>
<input
type="password"
placeholder={t('auth.password')}
/>
<button type="submit">
{t('auth.login')}
</button>
<a href="#">
{t('auth.forgotPassword')}
</a>
</form>
);
}
Trans Component (For Complex Content)
When you need to embed components inside translated strings - the Trans component documentation covers every edge case:
import { Trans } from 'react-i18next';
function TermsNotice() {
return (
<p>
<Trans i18nKey="auth.termsAgreement">
By signing up, you agree to our
<a href="/terms">Terms of Service</a>
and
<a href="/privacy">Privacy Policy</a>
</Trans>
</p>
);
}
Your translation file would contain:
{
"auth": {
"termsAgreement": "By signing up, you agree to our <1>Terms of Service</1> and <3>Privacy Policy</3>"
}
}
Language Switcher Component
Every i18n app needs a language switcher:
import { useTranslation } from 'react-i18next';
function LanguageSwitcher() {
const { i18n } = useTranslation();
const languages = [
{ code: 'en', name: 'English' },
{ code: 'es', name: 'Español' },
{ code: 'fr', name: 'Français' }
];
return (
<select
value={i18n.language}
onChange={(e) => i18n.changeLanguage(e.target.value)}
>
{languages.map(lang => (
<option key={lang.code} value={lang.code}>
{lang.name}
</option>
))}
</select>
);
}
Handling Plurals and Interpolation
This is where i18n gets tricky. Different languages have different pluralization rules.
Interpolation
Pass dynamic values into translations:
function WelcomeMessage({ userName, unreadCount }) {
const { t } = useTranslation();
return (
<div>
<h1>{t('dashboard.welcome', { name: userName })}</h1>
<p>{t('dashboard.unreadMessages', { count: unreadCount })}</p>
</div>
);
}
Your translation file:
{
"dashboard": {
"welcome": "Welcome back, {{name}}!",
"unreadMessages": "You have {{count}} unread messages"
}
}
Pluralization
React-i18next handles plural forms automatically using the Intl.PluralRules API:
{
"items": {
"cartCount_one": "{{count}} item in cart",
"cartCount_other": "{{count}} items in cart",
"cartCount_zero": "Your cart is empty"
}
}
Usage:
function CartSummary({ itemCount }) {
const { t } = useTranslation();
return <p>{t('items.cartCount', { count: itemCount })}</p>;
}
This automatically picks the right plural form based on the count and current language.
Automating Translations
Here’s where the magic happens. Manually managing translation files is tedious and error-prone. Copying English strings into Google Translate, pasting them back, and repeating for every language does not scale.
A localization platform like Crowdin integrates directly into your Git workflow, similar to the patterns covered in our localization workflow automation guide. Instead of manually exporting and importing translation files, Crowdin auto-syncs with your repository, detects new or changed strings, and coordinates AI and human translations through pull requests. This means translations stay in sync with your code - no more wondering if the Spanish version has the latest button labels.
The setup is straightforward: connect your repository, point it to your src/i18n/locales/ folder, select target languages, and enable auto-translation. From there, every push triggers the translation workflow automatically.
For a deeper comparison of localization platforms, see our guide to the best AI translation tools.
Best Practices and Tips
Based on common implementation patterns, here are the practices that prevent headaches:
1. Extract Strings Early
Don’t hardcode strings in components - this is the single biggest predictor of i18n project pain, as flagged in the best AI translation tools roundup. Even if you’re only supporting English initially:
// Bad
<button>Submit</button>
// Good
<button>{t('common.submit')}</button>
Adding i18n later is much harder than building it in from the start.
2. Use Namespaces for Large Apps
Split translations into multiple files by feature:
const { t } = useTranslation(['dashboard', 'common']);
return (
<div>
<h1>{t('dashboard:title')}</h1>
<button>{t('common:save')}</button>
</div>
);
3. Handle Missing Translations
Set up a fallback strategy in your i18n config - i18next’s fallback principles walk through every option:
i18n.init({
fallbackLng: 'en',
saveMissing: true, // Log missing translations in dev
missingKeyHandler: (lng, ns, key) => {
console.warn(`Missing translation: ${lng}/${ns}/${key}`);
}
});
4. Test with Long Strings
According to W3C internationalization research, German translations are often 30-40% longer than English. Test your UI with long strings:
{
"test": {
"longString": "Geschwindigkeitsbegrenzungsüberschreitungsverwarnung"
}
}
5. Extract Date and Number Formatting
Use i18next’s formatting features for locale-aware dates and numbers via the browser-native Intl APIs:
import { useTranslation } from 'react-i18next';
function PriceDisplay({ amount, date }) {
const { i18n } = useTranslation();
const formattedPrice = new Intl.NumberFormat(i18n.language, {
style: 'currency',
currency: 'USD'
}).format(amount);
const formattedDate = new Intl.DateTimeFormat(i18n.language, {
dateStyle: 'long'
}).format(date);
return (
<div>
<p>{formattedPrice}</p>
<p>{formattedDate}</p>
</div>
);
}
6. SEO for Multilingual Content
If you’re using React Router, set the HTML lang attribute dynamically:
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
function App() {
const { i18n } = useTranslation();
useEffect(() => {
document.documentElement.lang = i18n.language;
}, [i18n.language]);
return <div>{/* Your app content */}</div>;
}
This helps search engines understand which language version they’re indexing.
Conclusion
When evaluating React I18N Setup, Setting up React i18n properly takes some initial work, but it pays off massively when you need to expand to new markets. The key is choosing the right tools and patterns from the start.
React-i18next handles the runtime translation logic beautifully. Combined with an automation tool like Crowdin, you get a complete localization workflow that scales from 2 languages to 20 without adding manual work.
Start with these steps:
- Install react-i18next and set up basic configuration
- Organize translation files by feature/namespace
- Use the
useTranslationhook consistently - Connect Crowdin to automate translations
- Test with multiple languages early in development
Apps built with this setup routinely support 8-12 languages with minimal maintenance. It is a solved problem - you just need the right foundation.
How Should You Plan Your React i18n Setup from Day One?
Adding internationalization to a React app for the first time means hours of figuring out the right setup. The React i18n ecosystem has several moving parts and most tutorials skip the practical details you actually need. Treating react i18n setup as an afterthought is the most common mistake teams make, because retrofitting internationalization into a React app that hardcoded strings for six months is steep - every component revisited, every button label extracted, every hardcoded string moved into a namespaced JSON file.
A day-one react i18n setup removes that friction. Start by installing the required packages: react-i18next, i18next, and i18next-browser-languagedetector. Then create a new file src/i18n/config.js and import your translation files. Scaffold the src/i18n/locales/ folder with en/translation.json, es/translation.json, and fr/translation.json alongside your first component. Wire up the useTranslation hook in functional components from the start, so the recommended method for functional components is already the default pattern across your codebase.
Part of planning early is organizing keys by namespace instead of flat structures. Organize translation keys by feature or component - nav, auth, dashboard, errors - so nested structure makes it easy to find keys later. When working on the dashboard, all dashboard-related translations live under dashboard.*. Translation files frequently turn into unmaintainable messes with hundreds of flat keys, and namespace discipline on day one prevents that.
The harder cases also benefit from early decisions. Different languages have different pluralization rules, React-i18next handles plural forms automatically when you follow the cartCount_one and cartCount_other convention, and interpolation needs a consistent double-brace placeholder convention (such as count or name) across the team. Locale-aware dates and numbers should flow through Intl.NumberFormat and Intl.DateTimeFormat from the start. Setting a fallback language with fallbackLng: 'en' in your i18n config, and enabling saveMissing: true to log missing translations in dev, closes the loop.
- Scaffold the
src/i18n/locales/folder alongside your first component - Organize keys by namespace -
common,auth,dashboard- to avoid flat key dumps - Use the
useTranslationhook consistently across functional components - Set up a fallback strategy with
fallbackLng: 'en'before you need it - Connect Crowdin to your repository on day one so translation files stay in sync with your code
Frequently Asked Questions
What packages do you need for React i18n setup?
Install three npm packages to get started: react-i18next provides React bindings for i18next, i18next is the core internationalization framework, and i18next-browser-languagedetector automatically detects the user’s language from browser settings. Run npm install react-i18next i18next i18next-browser-languagedetector and then create your configuration file at src/i18n/config.js. The same package set powers Next.js internationalization and most Vite-based React projects.
How do you organize React i18n translation files?
Use a nested folder structure under src/i18n/locales/ with one subfolder per language - en/, es/, fr/ - each containing namespaced JSON files like translation.json, common.json, and dashboard.json. Inside each file, organize keys by feature or component (nav, auth, dashboard, errors) rather than a flat structure, so nested keys like auth.login make files easier to navigate. Treat each namespace as the contract for one feature area, and keep keys stable so that translation memory in your localization platform keeps matching as the codebase evolves.
How do you use translations in React components?
The recommended method for functional components is the useTranslation hook. Import it from react-i18next, call const { t } = useTranslation(), and use t('auth.login') to access translated strings. For content that needs embedded React components inside translated strings - like links inside terms agreements - use the Trans component with an i18nKey prop and inline child elements. Keep inline JSX inside Trans minimal: pass plain text and links, and avoid components that require runtime props, since translators see the raw string and rebuild it position by position.
How does React-i18next handle plurals and interpolation?
Pass dynamic values with double-brace placeholders so the call site t('dashboard.welcome', { name: userName }) pairs with the matching JSON entry that wraps the placeholder in double curly braces. For plurals, define keys with suffixes like cartCount_one, cartCount_other, and cartCount_zero, then call t('items.cartCount', { count: itemCount }). React-i18next automatically picks the right plural form based on the count and current language using the CLDR plural rules, so Arabic, Russian, and Polish all resolve correctly without extra logic.
How do you automate translation updates with Crowdin?
Connect your Git repository to Crowdin, point the integration at your src/i18n/locales/ folder, and configure source language (en/) plus target languages. Crowdin then opens a pull request whenever new strings appear or existing translations get updated, so your code review process gates translation changes the same way it gates any other code change. The setup typically takes under an hour for a small project, and once running, translators work entirely inside Crowdin while engineers stay focused on shipping features. For deeper coverage of automated localization workflows, see our localization workflow automation guide.
Should you use namespaces or a single translation file?
For projects under 200 strings, a single translation.json per language is fine. Past that threshold, split into namespaces - one per major feature area - so translation files stay reviewable and translators can focus on the slices that changed. Namespaces also let you lazy-load translations: only the dashboard feature’s translations need to ship when a user opens the dashboard. The useTranslation(['dashboard', 'common']) hook signature makes namespace selection explicit at each call site.
How do you test that React i18n is working correctly?
Add Cypress or Playwright tests that toggle the language switcher and assert that visible text changes. Pair that with a missing-translation reporter in development - set saveMissing: true in your i18n config so untranslated keys surface immediately. For unit tests, mock the useTranslation hook to return the key as the value, which keeps assertions stable across language changes. Visual regression tests catch the harder bugs: German strings that overflow buttons, right-to-left layouts that break in Arabic, and date formats that look wrong in unfamiliar locales.
Want to learn more about Crowdin?
Related Guides
- Localization Workflow Automation - Sibling guide on automating Crowdin pipelines end to end
Related Reading
Tools covered in this article:
- Crowdin - Git-native localization platform
More localization and development guides:
- Best AI Localization Tools 2026
- AI Translation Accuracy: What to Expect in Production
- Best AI Code Editors 2026
For more information about react i18n setup, see the resources below.