← Back to blog
··14 min read

next-intl vs react-i18next vs Lingui in 2026: Which i18n Library Should Your Next.js App Use

next-intlreact-i18nextLinguii18nInternationalizationNext.jsReactTypeScript
next-intl vs react-i18next vs Lingui in 2026: Which i18n Library Should Your Next.js App Use

Why Internationalization Is Harder Than a Dictionary

Adding a second language to a web app looks trivial until you actually do it. The first instinct is a plain object: map a key to a string per language and look it up. For a five-page site with English and one other language, that even works. Then reality arrives — pluralization that differs per language (English has two plural forms, Polish has three, Arabic has six), values interpolated into the middle of a sentence where word order flips between languages, per-locale formatting of numbers, dates, currencies, and relative times, and rich text where part of a translated sentence is a link or a bold span. A dictionary cannot express any of that; an i18n library exists precisely to.

A real internationalization (i18n) library gives you three things a hand-rolled map does not: a message format — usually ICU MessageFormat — that declares plurals, select rules, and interpolation correctly across languages; the machinery to load the right catalog, switch locales, and lazy-load translations so you do not ship every language to every user; and a contract for translators — catalog files they edit without touching code. On Next.js there is a fourth dimension a plain object cannot touch: rendering translated content on the server in React Server Components, and driving locale-prefixed routing like /en and /de.

In 2026 the three names that dominate the Next.js and TypeScript i18n conversation are next-intl, react-i18next, and Lingui. They get compared as rivals, but like most tooling trios they are not identical — they sit at different points on a single spectrum: *where was the library designed to run, and how do translations get authored and shipped?*

  • next-intl is i18n built for the Next.js App Router: designed around Server Components, with locale routing, middleware, ICU, and typed keys out of the box. It answers _"my app is Next.js and I want i18n that fits the App Router."_
  • react-i18next is the mature ecosystem that runs everywhere: the React binding for i18next, framework-agnostic, with the biggest plugin ecosystem and community. It answers _"I want the battle-tested option that works in any React environment."_
  • Lingui is the compiler-based option with the smallest bundle: a build-time macro extracts messages from your source and compiles compact catalogs. It answers _"I care about runtime size and a rigorous translation workflow."_
  • Put simply: next-intl fits Next.js, react-i18next fits everywhere, and Lingui fits the smallest bundle. The rest of this guide matches each to the question you actually have.

    Earth at night showing connected city lights across continents, representing an app serving many locales

    Earth at night showing connected city lights across continents, representing an app serving many locales

    Three Ways to Do i18n

    Before comparing features, it helps to see the three architectures underneath, because they explain every trade-off that follows.

    next-intl: i18n as a Next.js feature. There is no framework-agnostic core to adapt. next-intl was written for Next.js and, since the App Router arrived, around React Server Components. A Server Component can read messages and output finished translated HTML with no catalog crossing the wire; you opt into client-side messages only where you have interactivity. It ships a [locale] route segment pattern, middleware for locale detection and redirects, and locale-aware navigation helpers. The trade: you are committing to Next.js — which, if you are already there, is not a trade at all.

    react-i18next: a framework-agnostic core with a React binding. i18next is the established i18n framework of the JavaScript world; react-i18next is its React layer. Its defining trait is breadth — it runs in Next.js (Pages or App Router), Vite, Create React App, and React Native, with a large plugin ecosystem: HTTP backends, language detectors, ICU formatting, caching. It supports namespaces and lazy loading, and has by far the most Stack Overflow answers. The trade: a full Next.js setup is several packages layered together, and it was historically client-oriented, so server-first rendering takes more care (next-i18next added App Router support in 2026).

    Lingui: a compiler, not just a runtime. Lingui's model is different in kind. A build-time macro extracts messages directly from your source — you often write the default message inline, so the code is the source of truth — and a CLI compiles catalogs into a compact runtime format. The result is the smallest runtime bundle of the three and close adherence to the ICU standard. The trade: a heavier build setup (macros, a compile step) and a smaller community than i18next.

    The reason this matters: a Next.js-native library gives you server rendering and routing for free but only inside Next.js; a framework-agnostic one runs anywhere but asks you to assemble and adapt more; a compiler-based one ships the least code but adds a build step. Picking the wrong shape means fighting your framework instead of shipping your feature.

    next-intl: i18n Built for the App Router

    next-intl is the default answer when your app is Next.js and you are on the App Router. Its central advantage is that it renders in React Server Components, so translated content is produced on the server and message catalogs stay out of the client bundle unless you explicitly need them in an interactive component.

    tsx
    // app/[locale]/page.tsx — a Server Component, no catalog shipped to the client
    import { useTranslations } from "next-intl";
    
    export default function HomePage() {
      const t = useTranslations("Home");
      return <h1>{t("title")}</h1>; // rendered to HTML on the server
    }

    Messages use ICU format, so plurals, select, and interpolation are first-class, and next-intl pairs that with TypeScript inference — your message keys and their arguments are typed, so a typo or a missing key is a compile error, not a runtime blank. It also handles the App Router specifics that are otherwise fiddly: a [locale] route segment, middleware that detects the user's locale and redirects to the right prefix, and navigation helpers that keep the current language on every link.

    json
    // messages/en.json — the translator's contract, plain and editable
    {
      "Home": {
        "title": "Build once, ship everywhere",
        "items": "{count, plural, =0 {No items} one {# item} other {# items}}"
      }
    }

    The payoff: on the App Router, the amount of i18n code and catalog data that reaches the browser can be very small, because so much renders server-side — which is exactly the grain the App Router is designed for.

    Best when: you are building a new Next.js App Router app, you want locale routing and typed keys with minimal setup, and keeping the client bundle lean matters.

    react-i18next: The Mature Ecosystem That Runs Everywhere

    react-i18next is the default answer when you want the most established, most flexible option — or when Next.js is not the only place your code has to run. It is the React binding for i18next, and its strength is the ecosystem around it.

    tsx
    import { useTranslation } from "react-i18next";
    
    function Cart() {
      const { t } = useTranslation("cart");
      return <p>{t("items", { count })}</p>; // plurals + interpolation built in
    }

    What you get with the ecosystem: backend plugins that load translations over HTTP or from a CDN, a language detector that reads the browser, cookie, or path, namespaces so you can split catalogs by feature and lazy-load only what a view needs, and an ICU plugin when you need the full standard beyond i18next's own (very capable) plural and interpolation system. Because it is framework-agnostic, the same knowledge and often the same catalogs carry across a Next.js app, a Vite dashboard, and a React Native app.

    The cost is assembly. A production Next.js setup is i18next core plus the React binding plus a backend loader plus a detector — several packages, more configuration, and, because i18next grew up client-side, more care about which components are server vs client on the App Router. next-i18next v16 (2026) added proper App Router support, which closes much of that gap, but next-intl still starts closer to the server-first model.

    Best when: you need the biggest ecosystem and community, you must support non-Next.js targets too, or you want maximum flexibility to wire i18n into any backend or translation platform.

    Lingui: The Compiler-Based, Smallest-Bundle Option

    Lingui is the default answer when runtime bundle size and a rigorous, code-first translation workflow are the priority. Its model is a compiler: you write messages inline, and a build-time macro extracts them.

    tsx
    import { Trans } from "@lingui/react/macro";
    
    function Welcome({ name }: { name: string }) {
      // the English text IS the source of truth; the CLI extracts it
      return <Trans>Welcome back, {name}</Trans>;
    }

    Running the Lingui CLI scans your source, pulls out every message, and generates catalogs; a compile step turns them into a compact runtime format. Two things fall out of this. First, the runtime bundle is the smallest of the three, because the heavy lifting happened at build time. Second, completeness is checkable — the CLI knows exactly which messages exist and which locales are missing translations, so you can gate a release on "all strings translated," which teams with professional translators genuinely value. Lingui follows ICU closely, so plurals and select rules are correct by construction.

    The cost is setup and reach: you configure macros and a compile step (Lingui supports the App Router via its SWC plugin), and the community is smaller than i18next's, so there are fewer ready-made answers when something goes sideways.

    Best when: bundle size is a hard constraint, you want the source code to be the source of truth with automatic extraction, and translation completeness is a release gate.

    Developer workstation with code and terminal open, representing the extract-and-compile translation workflow

    Developer workstation with code and terminal open, representing the extract-and-compile translation workflow

    Head to Head: The Decisions That Actually Differ

    App Router and Server Components. next-intl is built for it and renders server-side by default. Lingui supports it via its SWC plugin. react-i18next works on it, and next-i18next v16 made that first-class in 2026, but it started life client-oriented. If server-first rendering with the least configuration is the goal, next-intl wins.

    Runtime bundle size. Roughly Lingui < next-intl < react-i18next on pure client weight — but next-intl's Server Component rendering can erase the gap for server-rendered content, and good catalog splitting matters more than base library size for most real apps.

    Message format. next-intl and Lingui speak ICU natively; react-i18next has its own capable system and reaches full ICU through the official plugin. For strict, standards-based ICU across a large team, next-intl or Lingui start you closer.

    Authoring workflow. next-intl and react-i18next are key-based — you define keys and translators fill them per language, which integrates cleanly with translation platforms. Lingui is source-message-based — the code is the source of truth and the CLI extracts, which gives automatic completeness checking. Both models are good; they suit different teams.

    TypeScript. next-intl leads on typed message keys catching missing references at compile time. Lingui and react-i18next both have solid TypeScript support, and react-i18next can type its resources with some setup. If you want your invalid keys to be red squiggles rather than blank UI (the same instinct behind schema validation with Zod), next-intl is the strongest here.

    Ecosystem and community. react-i18next is the clear leader — ~2.8M weekly downloads, the most plugins, the most answers. next-intl (~900K/week) is the fastest-growing as App Router adoption rises. Lingui (~300K/week) is smaller but stable and beloved by its users.

    Which Should You Ship?

    New Next.js App Router app → next-intl. It fits the framework, renders on the server, gives you locale routing and typed keys with the least glue, and keeps the client bundle small. This is the default for most modern Next.js starters.

    Must run beyond Next.js, or you want the biggest ecosystem → react-i18next. If the same i18n has to serve a Vite app or React Native too, or you want maximum plugin flexibility and the deepest well of community answers, its breadth is the deciding factor.

    Bundle size and translation rigor are the priority → Lingui. For a content-heavy or performance-focused product, or a team with professional translators and strict completeness gates, the compile-time workflow and tiny runtime are features, not overhead.

    The mistake to avoid is choosing on downloads alone. react-i18next's larger number reflects its age and reach across all of React, not that it is the best fit for a fresh App Router project — where next-intl is usually less work and less client JavaScript.

    Shipping i18n in a Template You Sell

    If you build templates and starters to sell, the i18n layer is judged the same way buyers judge everything else in the codebase: is it native to the stack, easy to extend, safe by default, and documented? A few rules make an internationalized template read as production-ready:

  • Match the stack. An App Router template should default to next-intl; reach for react-i18next only if cross-framework reach is part of the pitch, or Lingui if lean bundles and a strict pipeline are the selling point.
  • Ship editable catalogs. Message files belong as plain, editable catalogs — not strings buried in components — with at least two locales included so the buyer sees the pattern to copy.
  • Keep it in a thin layer. Locale switching, routing, and the translation setup should live in a small, documented module, not scattered through every component, so a buyer can swap or extend it without archaeology.
  • Never hardcode one language. Every user-facing string goes through the i18n layer from day one; retrofitting a hardcoded template is exactly the tax buyers do not want.
  • Document adding a locale. One short doc — where catalogs live, how to add a language, how routing picks the locale — does as much for perceived quality as the translated UI itself.
  • An i18n layer that is cleanly structured, typed where possible, trivial to extend to new languages, and documented signals the same care as clean data fetching, real validation, and a sensible component library choice — the details that separate a template that sells from one that sits.

    The Bottom Line

    All three libraries do the core job well: ICU-grade pluralization and interpolation, per-locale formatting, locale switching, and catalog splitting so you do not ship every language to every user. The decision is not "which one does i18n" — it is *where your code runs and how translations get authored*.

  • next-intl — the default for new Next.js App Router apps: server-first, locale routing built in, typed keys, minimal glue.
  • react-i18next — the mature, framework-agnostic ecosystem: runs everywhere, biggest community, most plugins, most flexible.
  • Lingui — the compiler-based, smallest-bundle option: code as the source of truth, automatic extraction, translation completeness as a release gate.
  • Pick next-intl unless you have a specific reason to reach past it — cross-framework reach pulls you to react-i18next, and a bundle-size-or-translation-rigor mandate pulls you to Lingui.

    Ready to turn what you build into income? List your SaaS or Next.js template on CodeCudos, see how i18n fits the wider stack in our best tech stack for web apps in 2026 guide, pick your framework with Next.js vs Remix vs TanStack Start, tighten your forms with Zod vs Yup vs Valibot, or make sure the whole build reads as production-ready.

    Frequently asked questions

    What is an i18n library and why not just swap strings with a plain object?

    An internationalization (i18n) library manages translated text and locale-specific formatting across your whole app — and the reason you reach for one instead of a hand-rolled dictionary is that real translation is far more than looking up a string by key. A naive approach is a big object mapping keys to strings per language, and for a tiny app that can work. It falls apart the moment you hit the things every production app needs: pluralization that differs per language (English has two plural forms, Polish has three, Arabic has six), gender and select rules, interpolating values into the middle of a sentence where word order changes between languages, formatting numbers, dates, currencies, and relative times per locale, and rich text where part of a translated sentence is a link or bold. A proper i18n library gives you a message format (usually ICU MessageFormat) that expresses all of that declaratively, plus the machinery around it: loading the right catalog, switching locales, lazy-loading translations so you do not ship every language to every user, extracting messages for translators, and detecting completeness. It also gives translators a stable contract — a catalog file they can edit without touching code. For a Next.js app there is an extra dimension a plain object cannot handle well: rendering translated content on the server (in Server Components) and driving locale-prefixed routing. next-intl, react-i18next, and Lingui are three mature answers to all of this, differing mainly in how tightly they integrate with Next.js, how big their ecosystem is, and how translations get authored and bundled.

    What is the core difference between next-intl, react-i18next, and Lingui?

    It comes down to where the library was designed to run and how translations are authored and shipped — and that split explains almost every other trade-off. next-intl is built specifically for Next.js and, since the App Router arrived, around React Server Components: it renders translated content on the server so message catalogs do not have to reach the browser, it ships locale routing and middleware for URL prefixes like /en and /de, it uses ICU message format, and it has first-class TypeScript inference for message keys. It is the most Next.js-native of the three and the least glue on an App Router project. react-i18next is the React binding for i18next, the most established i18n framework in the JavaScript world. Its defining trait is breadth: it is framework-agnostic and runs anywhere React runs, it has a large plugin ecosystem (HTTP backends, language detectors, ICU formatting, caching), it supports namespaces and lazy loading, and it has by far the biggest community. The trade is that a full Next.js setup is several libraries layered together and it was not originally App-Router-native, though next-i18next added App Router support in 2026. Lingui takes a different approach entirely: a build-time macro extracts messages directly from your source (you often write the default message inline, so the code is the source of truth), a CLI extracts and compiles catalogs to a compact runtime format, and the result is the smallest runtime bundle of the three with close adherence to the ICU standard. The spectrum: next-intl = Next.js-and-App-Router-native, react-i18next = mature framework-agnostic ecosystem, Lingui = compiler-based and smallest bundle. Pick based on your framework, your need for ecosystem breadth, and how much you care about runtime size and authoring workflow.

    Which one is best for the Next.js App Router and Server Components?

    For a new Next.js App Router project, next-intl is the default and the reason is architectural, not cosmetic: it was designed around React Server Components, so it can render translated content on the server and keep message catalogs out of the client bundle by default. That matters because the App Router's whole model is 'render on the server, ship less JavaScript' — and an i18n library that forces every translation into a client component works against that grain. With next-intl, a Server Component can read messages and output finished translated HTML with no locale data crossing the wire; you only opt into client-side messages where you genuinely have interactivity. It also handles the App Router specifics that are otherwise fiddly: a [locale] route segment, middleware that detects and redirects to the right locale prefix, and helpers that make next/navigation locale-aware so links keep the current language. react-i18next can absolutely work in the App Router — and next-i18next v16 in 2026 added proper App Router support — but historically i18next was client-oriented, so getting clean Server Component rendering takes more setup and care about which components are server vs client. Lingui also supports the App Router (via its SWC plugin) and its server story has improved, but next-intl remains the one built from the ground up for this exact environment. The honest summary: if your project is Next.js App Router and that is unlikely to change, next-intl gives you the most correct behavior with the least configuration; the other two are viable but ask more of you to match the server-first model.

    How do bundle size and performance compare across the three?

    Runtime bundle size is the dimension where the three genuinely diverge, and it maps directly to their architectures. Lingui is the smallest: because a compiler extracts and compiles your messages ahead of time into a compact format, the runtime it ships to the browser is tiny — this is its headline advantage, and for a performance-sensitive app or one shipping many locales it is a real edge. next-intl is lighter than a full i18next stack, and it has a second, larger lever: rendering in Server Components means the translation logic and catalogs for server-rendered content can stay on the server entirely, so the client downloads finished HTML rather than a message runtime plus catalogs. In practice, on the App Router, the amount of i18n code that reaches the browser can be very small precisely because so much can render server-side. react-i18next is the heaviest of the three in raw client terms, because a production Next.js setup layers several packages (i18next core, the React binding, a backend loader, a language detector, sometimes an ICU plugin) and, being historically client-oriented, more of that tends to run in the browser — though lazy-loading namespaces means you only fetch the translations a given view needs, which keeps it reasonable in practice. The nuance that matters: bundle size is not just the library, it is the catalogs, and all three let you split translations so you do not ship every language to every user. So the ranking on pure runtime weight is roughly Lingui < next-intl < react-i18next, but next-intl's server rendering can close or erase that gap for server-rendered content, and good catalog splitting matters more than the base library size for most real apps.

    What is ICU MessageFormat and do all three support pluralization and interpolation?

    ICU MessageFormat is the de facto standard syntax for writing translatable messages that handle the hard parts of human language — plurals, gender/select, nested choices, and value interpolation — in a way translators across languages can express correctly, and support for it is a key differentiator. The problem it solves: 'You have 1 message' vs 'You have 5 messages' is not a string swap, because different languages have different plural categories (one, few, many, other), and where the number and noun sit in the sentence changes per language. ICU lets you write one message like a plural block that picks the right form per locale, plus interpolation ({name}), number/date/currency formatting, and select statements for things like gender. next-intl uses ICU message format directly, so plurals, select, and rich interpolation are first-class, and it pairs that with TypeScript so your message keys and their arguments are typed. Lingui also follows ICU closely — it is one of its selling points — and its macros make writing ICU-correct messages ergonomic. react-i18next has its own interpolation and pluralization system by default (which covers most needs), and can use full ICU via an official i18next-icu plugin when you need the complete standard; so ICU is available but is an add-on rather than the native format. All three, therefore, handle pluralization and interpolation well enough for real apps; the difference is that next-intl and Lingui speak ICU natively while react-i18next reaches full ICU through a plugin. If strict, standards-based ICU across a large translation team is a hard requirement, next-intl or Lingui start you closer to it; if you just need solid plurals and interpolation, all three deliver.

    How does the translation workflow differ — keys vs source messages, and extraction?

    The day-to-day authoring workflow differs in a way that shapes how your team and translators actually work, and it splits along 'do you invent keys or use the source text as the message?' next-intl and react-i18next are both key-based: you define keys (often nested by namespace or page) in catalog files, reference them in code, and translators fill in each key per language. This is explicit and organized, works cleanly with translation-management platforms, and makes it obvious which strings exist — but it means maintaining a separate key namespace and keeping code and catalogs in sync, and it is on you (or a linter) to catch unused or missing keys. Lingui's defining workflow is different: you write the default message inline in your code (for example a t macro wrapping the English text), and a build-time extraction CLI scans your source, pulls out every message, and generates the catalogs automatically. The source code becomes the source of truth, extraction is a command you run, and completeness is checkable because the CLI knows exactly which messages exist and which locales are missing translations. That is a genuinely nice loop for teams with professional translators and strict completeness gates, at the cost of a heavier build setup (macros, a compile step) and the discipline of running extract/compile. react-i18next also has extraction tooling (i18next-parser and others) so you are not forced to hand-maintain every key, and next-intl's typed keys catch missing references at compile time. The practical read: if you want the code to be the source of truth and automatic extraction with completeness checking, Lingui is purpose-built for that; if you prefer explicit, organized keys and integration with translation platforms, next-intl and react-i18next fit that model, with next-intl adding type safety on top.

    Which i18n library should a SaaS boilerplate or template you sell ship with?

    For a boilerplate, SaaS starter, or template you intend to hand off or sell, the guidance mirrors every other infrastructure decision: default to what fits the stack the template already uses, is easy for the buyer to extend, is safe and cheap by default, and does not lock them into a workflow they did not ask for — and deviate only for a stated reason. If your template is a Next.js App Router project (the common case for modern starters), next-intl is usually the strongest default: it is the most Next.js-native, its Server Component rendering keeps the client bundle small, locale routing works out of the box so the buyer gets /en and /de URLs for free, and typed message keys make it hard for a buyer to reference a translation that does not exist — the lowest-friction path to an i18n-ready demo that runs on clone. If the template needs to support environments beyond Next.js, or its selling point is a mature, plugin-rich setup the buyer can bend to any backend or translation platform, react-i18next is the safer default because of its ecosystem breadth and the sheer amount of documentation and community answers a buyer can lean on. Lingui is the right default when the template's pitch includes lean bundles and a rigorous translation pipeline — a content-heavy or performance-focused product where the compile-time workflow is a feature. Whatever you choose, the resale rules are identical to any code you sell: ship the message catalogs as editable files (not buried in code), include at least two locales so the buyer sees the pattern, keep the locale-switching and routing logic in a thin, documented layer rather than scattered through components, do not hardcode a single language, and write clear docs on how to add a locale and where the catalogs live. An i18n layer that is cleanly structured, typed where possible, easy to extend to new languages, and documented does as much to make a codebase read as production-ready as the translated UI on top of it.

    Related guides

    Browse Quality-Scored Code

    Every listing on CodeCudos is analyzed for code quality, security, and documentation. Find production-ready components, templates, and apps — or sell your own code and keep 90%.

    Browse Marketplace →