---
createdAt: 2026-09-16
updatedAt: 2026-09-16
title: "How to pick the right React i18n library in 2026"
description: A decision guide for React internationalization. Which questions to answer before comparing react-i18next, react-intl, Lingui, use-intl, Paraglide and Intlayer, and what each choice costs in bundle size, typing and maintenance.
keywords:
- react i18n
- react internationalization
- react-i18next
- react-intl
- Lingui
- use-intl
- Paraglide
- Intlayer
- i18n library comparison
slugs:
- blog
- how-to-pick-react-i18n-library
author: aymericzip
---
# How to pick the right React i18n library
React ships no i18n primitive. The library you pick on day one decides how translations are stored, how they reach the bundle, and how much of the work stays yours for the next few years. Most teams pick by popularity, then discover the trade-offs at 2,000 keys.
This guide goes the other way: answer a few questions about your project first, then map the answers to the libraries that fit. It focuses on plain React (Vite, React Router, TanStack Start). Next.js has its own constraints, covered in the [Next.js comparison](https://github.com/aymericzip/intlayer/blob/main/docs/blog/en/next-i18next_vs_next-intl_vs_intlayer.md).

## Table of Contents
## Six questions to answer before comparing libraries
A feature table is useless without knowing which rows matter to you. Go through these first.
1. **How is the app rendered?** SPA only, SSR with hydration, or React Server Components. Context-based hooks work everywhere in an SPA. With RSC, a hook forces `"use client"` on every component that renders text, so you will need a server-side API too.
2. **Who writes the translations?** Developers, an in-house team using a TMS, an agency delivering ICU files, or an AI pipeline. This dictates the catalog format more than any API detail.
3. **How many locales and pages?** Two locales and five pages can afford to ship everything. Ten locales and fifty routes cannot, and the loading strategy becomes the main cost.
4. **Do you need types on keys?** A typo in `t("checkout.totl")` compiles in every key-based library unless you wire the types yourself. Decide whether that is acceptable.
5. **What does the string contain?** Plain text, plurals, or sentences with a `` in the middle. Rich content is where most APIs get awkward.
6. **How long will the project live?** A three-month prototype and a five-year product do not need the same amount of build tooling.
Write the answers down. Everything below refers back to them.
## The landscape in one picture
Fifteen years of JavaScript i18n fit in four architectural waves, and the React libraries you will compare come from different ones.

JSON catalogs loaded in memory, `t("a.b")` looked up at runtime, ICU or a custom syntax parsed in the browser. Largest ecosystems, heaviest runtimes, types are opt-in.
Messages extracted at build, compiled to compact catalogs, typed arguments. An extra build step (`extract`, `compile`) in exchange for smaller bundles.
Designed around SSR and Server Components. Render on the server, hydrate only what the client needs. Still key-based and centralized.
Content is compiled into tree-shakable functions or per-component dictionaries. Types are generated, missing translations fail the build, and AI translation runs from the CLI.
The [history of JavaScript i18n](https://github.com/aymericzip/intlayer/blob/main/docs/blog/en/history_of_i18n.md) details how each wave answered the previous one's problems.
## The decision that matters most: where content lives and when it loads
Every React i18n library has the same shape: a store, a provider, a hook. Whatever the provider receives ends up in the client bundle or in the hydration payload. So the two structural choices are:
- **Centralized or scoped content.** One `en.json` for the app, or one declaration per component (or per namespace).
- **Static or dynamic import.** Everything bundled at startup, or the active locale and route fetched on demand.
The graph below estimates the payload for a theoretical app of 1 to 10 pages, translated into 1 to 10 locales, with about 30 KB of text per page.

Centralized content with static imports grows with both axes: 10 pages times 10 locales is 300 KB of text on every page. Dynamic imports remove the locale axis. Scoping removes the page axis. Only the combination stays flat.
This is not a library property, it is a discipline property. `react-i18next` can be scoped with namespaces and lazy backends. `use-intl` can be split per route. But nothing enforces it, and a shared `