If you’re building a multilingual React Native (or web) app, you’ve probably tried react-i18next, i18n-js, LinguiJS, or similar libraries.
But in every project, the same issues come up:
❌ Unused key-value pairs are never removed
❌ Content gets duplicated
❌ Ensuring format consistency across languages is painful
❌ i18next doesn’t generate TypeScript types by default – so t("my.key") won’t throw even if it’s been deleted
❌ Localization platforms like Lokalise or Locize get expensive fast
Frustrated by these challenges, I waited for a better solution... then decided to build one myself: Intlayer.
✨ Key points
✅ Works with React Native and Lynx
✅ Easy integration
✅ Localized content close to your components
✅ Autogenerated TypeScript types
✅ Define content in JSON, JS, or TS
✅ Embed external files (Markdown, TXT...)
✅ Fetch and type remote content instantly
✅ Native CMS compatibility for editing content externally
⚡ Getting Started (React Native)
1️⃣ Install
npm install intlayer react-intlayer react-native-intlayer
2️⃣ Configure Locales
Create intlayer.config.ts at the root:
import { Locales, type IntlayerConfig } from "intlayer"; const config: IntlayerConfig = { internationalization: { locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH], defaultLocale: Locales.ENGLISH, }, }; export default config;
3️⃣ Add Metro Support
In metro.config.js:
const { getDefaultConfig } = require("expo/metro-config"); const { configMetroIntlayer } = require("react-native-intlayer/metro"); module.exports = (async () => { const defaultConfig = getDefaultConfig(__dirname); return await configMetroIntlayer(defaultConfig); })();
4️⃣ Wrap Your App
In _layout.tsx:
import { Stack } from "expo-router"; import { getLocales } from "expo-localization"; import { IntlayerProviderContent } from "react-intlayer"; import { intlayerPolyfill } from "react-native-intlayer"; intlayerPolyfill(); const RootLayout = () => { return ( <IntlayerProviderContent defaultLocale={getLocales()[0]?.languageTag}> <Stack> <Stack.Screen name="(tabs)" /> </Stack> </IntlayerProviderContent> ); }; export default RootLayout;
5️⃣ Define Localized Content by Component
Keep translations close to your UI:
import { t, md, file, type Dictionary } from "intlayer"; const homeScreenContent = { key: "home-screen", content: { title: t({ en: "My Title", fr: "Mon titre", es: "Mi título", }), description: t({ en: md(file("./myDescription.en.md")), fr: md(file("./myDescription.fr.md")), es: md(file("./myDescription.es.md")), }), contentFetch: fetch("https://example.com").then((res) => res.text()), }, } satisfies Dictionary; export default homeScreenContent;
Then use them like this:
import { Text, View } from "react-native"; import { useIntlayer } from "react-intlayer"; const MyComponent = () => { const { title, description, contentFetch } = useIntlayer("my-component"); return ( <View> <Text>{title}</Text> <Text>{description}</Text> <Text>{contentFetch}</Text> </View> ); };
🔄 Switch Languages at Runtime
import { View, Text, TouchableOpacity } from "react-native"; import { getLocaleName } from "intlayer"; import { useLocale } from "react-intlayer"; const LocaleSwitcher = () => { const { setLocale, availableLocales } = useLocale(); return ( <View> {availableLocales.map((locale) => ( <TouchableOpacity key={locale} onPress={() => setLocale(locale)}> <Text>{getLocaleName(locale)}</Text> </TouchableOpacity> ))} </View> ); }; export default LocaleSwitcher;