# General Translation Knowledge Base: react-intl alternatives: General Translation vs. react-intl
URL: https://generaltranslation.com/en-GB/kb/general-translation-vs-react-intl.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: General Translation lets React teams keep ICU message syntax while adding translation generation, shared terminology, and Dashboard review. react-intl supplies formatting and FormatJS catalogue tooling; General Translation produces the translations too.
Category: Internationalisation
Last updated: 2026-10-16
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-16

react-intl is the [React library from FormatJS](https://formatjs.github.io/docs/react-intl). You write each message inline as a `defaultMessage` in ICU MessageFormat, add a `description` for translators, collect every message into a JSON catalogue with `formatjs extract`, and render translated catalogues through `IntlProvider`. It doesn&#39;t translate anything: the extracted catalogue goes to you, your translators or a translation management system (TMS), and the translated files come back to be compiled. General Translation&#39;s libraries render ICU messages too, but General Translation also generates the translations.

## What react-intl does well

* **ICU MessageFormat throughout.** [Plurals with `#` and exact matches such as `=0`, `select`, and `selectordinal`](https://formatjs.github.io/docs/core-concepts/icu-syntax), built on the browser&#39;s `Intl` APIs.
* **Inline source text.** FormatJS [recommends writing `defaultMessage` and `description` inline](https://formatjs.github.io/docs/getting-started/message-declaration) and letting `babel-plugin-formatjs` or `@swc/plugin-formatjs` generate message IDs, so nobody has to invent keys.
* **Tooling for a TMS workflow.** [`formatjs extract`](https://formatjs.github.io/docs/tooling/cli) writes the catalogue, built-in formatters produce files for Crowdin, Lokalise, Smartling and Transifex, `formatjs compile` pre-parses messages, and `formatjs verify` checks catalogues for missing or extra keys.
* **TypeScript.** [Version 12 can type each message&#39;s ICU arguments](https://formatjs.github.io/docs/react-intl/upgrade-guide-12.x).
* **Active maintenance.** [Version 12 shipped in September 2026](https://github.com/formatjs/formatjs/blob/main/packages/react-intl/CHANGELOG.md) under the BSD-3-Clause licence.

## What react-intl leaves to you

Here&#39;s a small inbox built with react-intl. The English stays in the component as `defaultMessage`:

```jsx title="Inbox.jsx"
import { FormattedMessage } from 'react-intl';

export function Inbox({ count }) {
  return (
    <section>
      <h1>
        <FormattedMessage defaultMessage="Inbox" description="Heading of the inbox page" />
      </h1>
      <p>
        <FormattedMessage
          defaultMessage="You have {count, plural, one {# unread message} other {# unread messages}}."
          description="Number of unread messages in the inbox"
          values={{ count }}
        />
      </p>
    </section>
  );
}
```

`formatjs extract` turns those messages into a catalogue keyed by generated IDs:

```json title="lang/en.json"
{
  "U5vJOY": {
    "defaultMessage": "You have {count, plural, one {# unread message} other {# unread messages}}.",
    "description": "Number of unread messages in the inbox"
  },
  "gBGgGs": {
    "defaultMessage": "Inbox",
    "description": "Heading of the inbox page"
  }
}
```

Each target language needs translated messages with their ICU syntax intact. `IntlProvider` accepts message strings or precompiled messages; `formatjs compile` prepares the latter. The extracted descriptors carry each message and its description. For the generated IDs in this example, configure the Babel or SWC plugin in your build. In the [Next.js App Router](https://formatjs.github.io/docs/guides/nextjs-app-router), react-intl&#39;s components run in client components, while server code formats messages through `createIntl` from `react-intl/server`.

## Keep ICU messages and generate the translations

General Translation&#39;s libraries do the same job as react-intl: they render ICU messages, apply each locale&#39;s plural rules, and format numbers, currencies and dates. Your ICU strings carry over almost unchanged, because [`useGT`](/docs/react/reference/hooks/use-gt) accepts the same syntax:

```tsx title="Inbox.tsx"
import { useGT } from 'gt-react';

export function Inbox({ count }: { count: number }) {
  const gt = useGT();
  return (
    <section>
      <h1>{gt('Inbox', { $context: 'Heading of the inbox page' })}</h1>
      <p>
        {gt('You have {count, plural, one {# unread message} other {# unread messages}}.', {
          count,
          $context: 'Number of unread messages in the inbox',
        })}
      </p>
    </section>
  );
}
```

[`npx gt translate`](/docs/cli/reference/commands/translate) extracts these strings, generates translations using their context notes, and writes the target catalogues in a single command. The example keeps both FormatJS descriptions as `$context`. Shared [Glossary terms and Custom Prompts](/docs/platform/dashboard/guides/defining-context-for-translations) guide terminology and tone, and reviewers edit the results in the [Dashboard](/docs/platform/dashboard/guides/reviewing-translations).

The `#` is formatted for each locale, so a count of 12345 reads as &quot;12,345&quot; in English and &quot;12.345&quot; in Spanish. For JSX with markup, [`<T>`](/docs/react/reference/components/t) translates the whole marked block, elements included. In `gt-next`, it works directly in Server Components.

Ramp&#39;s dashboard, landing page and iOS app ship in 11+ locales with General Translation. (See [production deployments](https://generaltranslation.com/enterprise).)

## react-intl vs. General Translation at a glance

| Feature                 | react-intl                                                                                            | General Translation                                                                                                        |
| ----------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| How you write copy      | Inline `defaultMessage` in ICU, extracted to a JSON catalogue                                         | Inline JSX in [`<T>`](/docs/react/reference/components/t), or ICU strings in [`useGT`](/docs/react/reference/hooks/use-gt) |
| Translates your content | No                                                                                                    | Yes, with [`npx gt translate`](/docs/cli/reference/commands/translate)                                                     |
| Catalogue workflow      | Extract messages, translate through your chosen workflow, optionally precompile; plugin generates IDs | [`npx gt translate`](/docs/cli/reference/commands/translate) extracts and translates before your build                     |
| Translation context     | The message and its description                                                                       | ICU strings with `$context`; marked JSX structure with inline components; shared Glossary and Custom Prompts               |
| Next.js App Router      | Components in client components; `createIntl` on the server                                           | [`<T>`](/docs/react/reference/components/t) in Server and Client Components                                                |
| Licence                 | [BSD-3-Clause](https://github.com/formatjs/formatjs/blob/main/packages/react-intl/LICENSE.md)         | MIT-licensed JavaScript libraries and CLI                                                                                  |

Keep react-intl when your ICU catalogues and TMS formatters already work, when you rely on its typed message arguments or `formatjs verify`, or when you use FormatJS outside React. Choose General Translation to keep ICU authoring while making translation generation and review part of your development workflow.

## Moving from react-intl

Move one component or route at a time. Replace each `<FormattedMessage>` with a [`useGT`](/docs/react/reference/hooks/use-gt) call that keeps its ICU string and passes its description as `$context`, as shown above. For rich JSX, use [`<T context="…">`](/docs/react/reference/components/t#context). Continue loading existing react-intl catalogues for components you have not yet moved. Once the last component has been moved, remove the FormatJS plugin and the extract and compile scripts. (See [Migrating i18n libraries](/docs/react/guides/migrating-i18n-libraries).)

## Other react-intl alternatives

Teams moving away from react-intl also compare these libraries:

* **i18next:** renders keyed catalogues with plural selection and, together with react-i18next and next-i18next, is a common choice for React and Next.js apps. (See [General Translation vs. i18next](/kb/general-translation-vs-i18next).)
* **Lingui:** keeps copy inline and compiles messages into catalogues with a small runtime. (See [General Translation vs. Lingui](/kb/general-translation-vs-lingui).)
* **next-intl:** renders catalogue messages in Next.js App Router apps, including Server Components. (See [next-intl vs. gt-next](/kb/next-intl-vs-gt-next).)
* **Paraglide JS:** compiles catalogues into typed, tree-shakable message functions. (See [General Translation vs. Paraglide JS](/kb/general-translation-vs-paraglide).)
* **Intlayer:** keeps each component&#39;s content in a typed declaration file, with every language side by side. (See [General Translation vs. Intlayer](/kb/general-translation-vs-intlayer).)

## Key takeaways

* react-intl formats ICU messages and provides catalogue tooling; translations are generated through a separate workflow.
* General Translation extracts and translates in a single CLI command, preserving description context and giving reviewers a Dashboard editor.
* Your ICU strings carry over to [`useGT`](/docs/react/reference/hooks/use-gt) almost unchanged, one component at a time.

## Related questions

- [General Translation vs. i18next, react-intl, and next-intl: which i18n library should you use?](https://generaltranslation.com/en-GB/kb/general-translation-vs-i18n-libraries.mdx): Open-source libraries such as i18next, react-intl, and next-intl don't translate anything. They display translations you supply in a catalogue file for each language, written by you, your translators or a separate service. General Translation's libraries are open source too and do the same job, but General Translation also does the translating, using your code as context.
- [i18next alternatives: General Translation vs. i18next](https://generaltranslation.com/en-GB/kb/general-translation-vs-i18next.mdx): Choose General Translation to keep copy inline in React components and generate translations with its JSX structure and product context. You can also keep i18next and use General Translation to translate existing catalogues. i18next supplies message rendering and plural selection; translation generation comes from a separate service.
- [General Translation vs. Lingui: which should you use?](https://generaltranslation.com/en-GB/kb/general-translation-vs-lingui.mdx): Choose General Translation to keep copy inline like Lingui and generate the translations with your JSX structure as context. Lingui extracts inline messages into catalogues for your translators or a connected service to translate. General Translation's CLI updates translations as your code changes, and your team reviews them in the Dashboard.
- [General Translation vs. vue-i18n: which should you use?](https://generaltranslation.com/en-GB/kb/general-translation-vs-vue-i18n.mdx): General Translation lets Vue teams write source copy directly in templates and generate translations with the CLI. Its gt-vue plugin renders that content with locale-aware plural and number formatting. Choose it to remove keyed source-file maintenance; vue-i18n remains a good fit for teams using Nuxt's i18n module and an established catalogue workflow.
- [How do I handle plurals in React?](https://generaltranslation.com/en-GB/kb/how-to-handle-plurals-in-react.mdx): Don't hard-code plural logic with checks such as n === 1. A small helper is enough for English-only apps, but multilingual apps need locale-aware plural rules, which an i18n component such as `<Plural>` from gt-react or gt-next handles for every language.

## Previous and next questions

- Previous question: [Phrase alternatives: General Translation vs. Phrase](https://generaltranslation.com/en-GB/kb/general-translation-vs-phrase.mdx)
- Next question: [Should you build your own i18n or use General Translation?](https://generaltranslation.com/en-GB/kb/general-translation-vs-in-house-i18n.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/en-GB/kb/sitemap.md) for every article.
