Blog

The Game-Changer for i18next & TypeScript: Introducing the Selector API!

The Game-Changer for i18next & TypeScript: Introducing the Selector API!

August 20, 2025

A couple of years ago, we wrote about getting the most out of i18next with TypeScript. Today, that story gets a revolutionary new chapter. We're incredibly excited to announce a new feature that fundamentally improves the developer experience: the selector API.

This feature, which closes a couple of long-standing TypeScript issues, was born from a brilliant idea and a monumental effort by Andrew Jarrett. It's now available in the latest versions of i18next and react-i18next, and it's about to change the way you work with your translations.

Why a Selector API?

If you've worked with a lot of translations with i18next and TypeScript, you're maybe familiar with the challenges. String-based keys like 'my.nested.key' may lack autocompletion, and may make navigating from your code to the translation file tedious. The new selector API solves all of this.

Instead of a string, you now pass a function to t:

// Before
i18next.t('my.nested.key');

// After
i18next.t($ => $.my.nested.key);

This seemingly small change unlocks a massive suite of benefits.

Unparalleled Developer Experience

  • Autocompletion & Discoverability: Your IDE now understands your translation structure. No more guessing keys or keeping a translation file open on a second monitor.
  • "Go to Definition" Just Works: You can now instantly jump from a translation's usage in your code directly to its definition in your resource file. This makes troubleshooting and staying in your development flow easier than ever.
  • No More "Doom Scrolling": For projects with hundreds or thousands of keys, the autocompletion list used to be a nightmare. The selector API allows you to explore your translations one level at a time, making it effortless to find what you need.
  • Preserved JSDoc Comments: Any comments or annotations you've written for your translations are now visible right at the call site, providing immediate context.

Ludicrous Type-Level Performance 🚀

A major pain point for projects with large translation files was performance. The type-level overhead could slow down IDEs to a crawl and even crash the TypeScript compiler. The selector API obliterates this problem.

  • With enableSelector: true, you'll see an improvement in type-level performance.
  • For massive projects, the new optimize mode is a game-changer. By setting enableSelector: 'optimize', you get a staggering improvement.

This means i18next is now capable of handling arbitrarily large translation sets with snappy IDE performance and no build-time errors.

How to Get Started

Ready to upgrade? Here's what you need to know.

1. Enable the Feature

In your i18next configuration, add the enableSelector option:

import i18next from 'i18next';

i18next.init({
  // For most projects
  enableSelector: true,
  
  // Or for very large projects
  // enableSelector: 'optimize',
  
  // ... your other options
});

‍

2. Migrate Your Code (The Easy Way)

The best part? There's a codemod to automate the migration for you! Andrew Jarrett built @i18next-selector/codemod to handle the syntax changes automatically. We strongly recommend using it to make your transition seamless.

3. Manual Migration Guide

If you prefer to migrate manually, here are the key changes to be aware of:

  • Namespaces: The namespace is now an explicit option.
// BEFORE:
t('a.b.c', { ns: 'myNamespace' });
// or t('myNamespace:a.b.c');

// AFTER:
t($ => $.a.b.c, { ns: 'myNamespace' });

‍

  • Default Values: The defaultValue is now passed through the options object. This also makes the return type a safer union of the key's type and the default value's type.
// BEFORE:
t('a.b.c', 'some default value');

// AFTER:
t($ => $.a.b.c, { defaultValue: 'some default value' });

‍

  • Key Fallbacks: Instead of passing an array of keys, you now provide fallbacks by calling t again within the defaultValue.
// BEFORE:
t(['error.404', 'error.unspecific']);

// AFTER:
t($ => $.error['404'], { defaultValue: t($ => $.error.unspecific) });

‍

A Massive Thank You to the Community

This incredible feature would not have been possible without the passion and dedication of several key individuals.

First and foremost, a massive thank you to Andrew Jarrett. He not only proposed the initial idea but also single-handedly implemented the entire feature, wrote the codemod for migration, and updated all the documentation. His commitment to improving the i18next developer experience has been truly inspiring.

A special thanks also goes to Marco Pasqualetti, who provided extensive, high-quality feedback and thoroughly tested every aspect of the new API to ensure it was robust and reliable.

And finally, thank you to the entire i18next community for your continued support. We can't wait to see how this new API empowers you to build even better, more type-safe, and more maintainable applications.

Go ahead and update to the latest versions of i18next and react-i18next to give it a try!

‍