đ React i18n TS Support
Introduction
The built-in type definitions of i18next are at your disposal. To get full intellisense and catch type errors before they happen, we can use as const to achieve full type safety for the t function.
Without this setup, the t function accepts any string as a key. That means a typo like t('welcom.title') instead of t('welcome.title') silently returns the key itself at runtime with no compile-time warning. With type safety in place, TypeScript flags unknown keys immediately in your editor.
Depending on the size of your translation sets and the machine you're rocking, the benefits of type safety and intellisense might slow down your development experience, something I noticed first hand with 4k+ translations when testing.
I do have some ideas for only indexing translation files by the current file system directory or page route but haven't figured out an elegant working solution. More to come on this if I figure out an elegant solution.
Ensure that the "strict" flag or "strictNullChecks" is set to true in your tsconfig compilerOptions.
If your project involves multiple i18next instances with different translation resources, you likely won't be able to use type-safe translations. This guide is for single language TS intellisense.
Translations setup
Install dependencies: npm install i18next react-i18next
Create your translations file en-us.jsonwith the default namespace key as "translation":
1{2 "translation": {3 "article": {4 "title": "i18n TypeScript support",5 "subheader": "The power of 'as const'"6 }7 }8}
Create your file i18n.ts, this is a lightweight config example, but there are lots of additional options to support things like automatic language detection, please see i18n docs.
1import i18n from 'i18next';2import { initReactI18next } from 'react-i18next';3import * as en from './translations/en-us.json';45export const defaultNS = 'translation';6export const resources = {7 en,8} as const;910i18n.use(initReactI18next).init({11 resources,12 defaultNS,13 ns: [defaultNS],14 fallbackLng: 'en',15 debug: false,16 returnNull: false,17});1819const t = i18n.t.bind(i18n);20export { t };2122export default i18n;
Declaration file
Expand TypeScript definitions for i18next through the use of Type Augmentation and Merging Interfaces. Begin by crafting a declaration file global.d.ts.
1import { resources, defaultNS } from './i18n';23declare module 'i18next' {4 interface CustomTypeOptions {5 defaultNS: typeof defaultNS;6 resources: (typeof resources)['en'];7 returnNull: false;8 }9}
Translations hook
Now we're all set up, we can import the useTranslation hook inside our React components. TypeScript will now autocomplete translation keys and flag any that don't exist in your JSON file.
1import { FC } from 'react';2import { useTranslation } from 'react-i18next';34const Example: FC = () => {5 const { t } = useTranslation();67 return (8 <p>{t('article.title')}</p>9 );10};1112export default Example;
Or outside a component:
1// from our i18n.ts setup file, not node_modules2import { t } from './i18n';34const dynamicConfig = {5 id: 'title',6 text: t('article.title')7}
One thing worth knowing: if you rename or delete a key in your JSON file, TypeScript will immediately show an error on every call site that references that key. That's the payoff for this setup. Refactoring translations becomes safe because the compiler tells you exactly what broke.
Was this useful?
Thanks for reading! Time for one more?
const { userId } = useLocalSearchParams()useEffect(() => {getUserDetails(userId).then(setUser)}, [userId])
đϏ Expo & Expo Router
21 Jan 2024 âĸ đ 30 min read âĸ Updated 16 Mar 2026Expo, EAS & Expo Router: one codebase covering web, Android, and iOS, with managed workflows, OTA updates, and file-based routing.
// arrow: 'this' is always the classlogName = () => console.log(this.name)// function: 'this' depends on callerlogName() { console.log(this.name) }
đš Regular Function vs Arrow Function
17 Aug 2023 âĸ đ 9 min read âĸ Updated 15 Mar 2026Learn the differences between normal functions and ES6 arrow functions.