npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

localize-react

v2.2.0

Published

Tiny, type-safe React i18n library built on Context and hooks — dual ESM + CJS, zero runtime deps.

Downloads

1,045

Readme

localize-react

React i18n, without the weight.

Tiny, type-safe React i18n built on Context and hooks. ~1.1 kB brotli · 0 runtime deps · dual ESM + CJS · React 19 ready.

Docs · Quickstart · API · Recipes · Migration v1 → v2

npm downloads CI coverage bundle types license


import { LocalizationProvider, useLocalize } from 'localize-react';

const translations = {
  en: { hello: 'Hi {{name}}!' },
  es: { hello: '¡Hola {{name}}!' },
  ja: { hello: '{{name}}さん、こんにちは!' },
};

function Greeting() {
  const { translate } = useLocalize();
  return <h1>{translate('hello', { name: 'Alex' })}</h1>;
}

export default function App() {
  return (
    <LocalizationProvider locale="en" translations={translations}>
      <Greeting />
    </LocalizationProvider>
  );
}

That's the whole API. Three exports, no plugins, no extraction toolchain. Ship it.


✨ Why?

Most React i18n libraries are 30–80 kB and bring opinions about plural rules, ICU MessageFormat, async loading, and TMS workflows. localize-react is the smallest thing that works.

It does exactly what a frontend most often needs:

  • A nested translations tree, keyed by locale.
  • Dot-path lookups ('cart.summary').
  • {{name}}-style interpolation.
  • A graceful fallback when keys are missing.

For everything else (plurals, currency, dates) — reach for the platform: Intl.PluralRules, Intl.NumberFormat, Intl.DateTimeFormat. Free, fast, already in the browser. See the Intl formatters recipe.

📦 Specs

| Property | Value | | -------------------- | ------------------------------------------------------ | | Bundle (brotli) | 1.13 kB ESM · 1.22 kB CJS | | Runtime dependencies | 0 | | Source | Strict TypeScript 6 | | Module formats | ESM + CJS with proper exports/types conditions | | Tree-shaking | sideEffects: false | | Peer range | React >= 16.8 < 20 (tested in CI through React 19) | | Node | >= 20.19 (CI on 20, 22, 24 × Linux/macOS/Windows) | | Test coverage | 100 % statements · 100 % functions · 98 % branches | | Type-checked exports | Validated by publint + @arethetypeswrong/cli in CI |

🚀 Install

npm install localize-react
# or: pnpm add localize-react · yarn add localize-react · bun add localize-react

⚡️ Quickstart

1. Define translations

export const translations = {
  en: {
    greeting: { hello: 'Hi {{name}}!' },
    cart: { summary: '{{count}} items, {{total}} total' },
  },
  es: {
    greeting: { hello: '¡Hola {{name}}!' },
    cart: { summary: '{{count}} artículos, {{total}} total' },
  },
} as const;

2. Mount the provider

import { LocalizationProvider } from 'localize-react';
import { translations } from './i18n/translations';

export function App() {
  return (
    <LocalizationProvider locale="en" translations={translations}>
      <Shell />
    </LocalizationProvider>
  );
}

3. Translate, two ways

import { Message, useLocalize } from 'localize-react';

// Hook
function Cart() {
  const { translate } = useLocalize();
  return <p>{translate('cart.summary', { count: 3, total: '$42.00' })}</p>;
}

// Component
function CartHeader() {
  return (
    <h1>
      <Message descriptor="greeting.hello" values={{ name: 'Alex' }} />
    </h1>
  );
}

That's the whole story. Full docs at yankouskia.github.io/localize-react.

🛡 Fully type-safe with createLocalization()

Want the compiler to catch missing keys, typo'd descriptors, and forgotten {{tokens}}? Wrap your translations once and use the typed bindings:

import { createLocalization } from 'localize-react';

const translations = {
  en: { greeting: 'Hi {{name}}!', cart: { checkout: 'Checkout' } },
  es: { greeting: '¡Hola {{name}}!', cart: { checkout: 'Pagar' } },
} as const;

export const { LocalizationProvider, useLocalize, Message } =
  createLocalization(translations);

// inside a component — `translate` autocompletes descriptors and requires {{name}}:
function Greeting() {
  const { translate } = useLocalize();
  return <h1>{translate('greeting', { name: 'Alex' })}</h1>; // ✅
  // translate('greting');                  ❌ Type '"greting"' is not assignable
  // translate('greeting');                 ❌ Property 'name' is missing
}

A pure compile-time wrapper — no runtime cost, no second cache. Full guide: Type-safe API.

🎨 Rich content with <RichMessage />

Need a link or component inside a translated sentence? <RichMessage /> returns a ReactNode and lets values include React nodes, so a single translation can host inline rich content without splitting the sentence across JSX:

import { RichMessage } from 'localize-react';

// en: 'By signing up, you agree to our {{link}}.'
// de: 'Mit der Anmeldung stimmen Sie unseren {{link}} zu.'
function Footer() {
  return (
    <p>
      <RichMessage
        descriptor="signup.terms"
        values={{ link: <a href="/terms">Terms of Service</a> }}
      />
    </p>
  );
}

The link lands wherever the translator put {{link}} — German word order, Japanese particles, RTL flow, all handled by the translation. Strings and numbers are still accepted (and coerced) so you can mix them freely. Available as a typed export from createLocalization() too. Full recipe: Rich content.

🧠 Concept in one screen

| Operation | API | | ------------------------ | -------------------------------------------------------- | | Mount translations | <LocalizationProvider locale translations> | | Translate (hook) | useLocalize().translate(descriptor, values?, default?) | | Translate (component) | <Message descriptor values? defaultMessage? /> | | Translate with JSX | <RichMessage descriptor values? defaultMessage? /> | | Switch locale at runtime | Re-render with a new locale prop | | Missing key | Renders defaultMessage ?? descriptor (never throws) | | Nested lookup | translate('a.b.c') walks the tree | | Interpolation | {{token}} — literal replacement, safe with regex chars | | Locale normalization | En-USen_usen |

📚 Recipes

Real-world patterns, fully documented on the site:

🥊 How it compares

| | localize-react | react-i18next | react-intl | lingui | | -------------------- | ------------------ | ------------- | ---------- | --------- | | Bundle (brotli) | ~1.1 kB | ~17 kB | ~38 kB | ~9 kB | | Runtime deps | 0 | several | several | one macro | | Pluralization (CLDR) | Use Intl | ✅ | ✅ (ICU) | ✅ (ICU) | | Number / date format | Use Intl | Optional | ✅ | ✅ | | ICU MessageFormat | ❌ | ✅ | ✅ | ✅ | | Lazy locale loading | DIY | ✅ | ✅ | ✅ | | Auto extraction | ❌ | ✅ | CLI | CLI | | TypeScript-first | ✅ | ✅ | ✅ | ✅ | | Learning curve | Tiny | Medium | Medium | Medium |

Use localize-react when you want a hook + a tag. Reach for the others when CLDR plurals, ICU MessageFormat, or a TMS workflow matter — they're all great at what they do.

🛡 Production-ready

  • Types ship inside the package — no @types/localize-react to chase.
  • Provenance attestation on every published version (npm OIDC trusted publishing).
  • CodeQL runs on every PR; CI matrix exercises Node 20/22/24 × Linux/macOS/Windows.
  • Size budget enforced — < 2 kB ESM, < 2.5 kB CJS, checked on every PR with size-limit.
  • No dynamic require, no eval, no regex from user input — interpolation is literal replaceAll.

📖 v1 → v2

The runtime API is unchanged. v2 modernizes the toolchain (strict TS 6, dual ESM+CJS, React 19 peer, GitHub Actions + Changesets). One soft TypeScript regression in exactOptionalPropertyTypes mode — see the migration guide.

🤝 Contributing

PRs welcome. See CONTRIBUTING.md for the setup + release flow. Security reports: please open a private security advisory rather than a public issue.

If you'd like to support the project, sponsoring helps a lot.

📄 License

MIT © Aliaksandr Yankouski