Technology Apr 16, 2026 · 4 min read

Implementing RTL (Right-to-Left) in React Native Expo - A Step-by-Step Guide

Supporting Right-to-Left (RTL) languages like Arabic and Hebrew is an important part of building globally accessible mobile apps. React Native already includes RTL support through I18nManager, but getting everything to work smoothly in an Expo-managed app usually takes a bit more setup. In this gui...

DE
DEV Community
by GeekyAnts Inc
Implementing RTL (Right-to-Left) in React Native Expo - A Step-by-Step Guide

Supporting Right-to-Left (RTL) languages like Arabic and Hebrew is an important part of building globally accessible mobile apps. React Native already includes RTL support through I18nManager, but getting everything to work smoothly in an Expo-managed app usually takes a bit more setup.

In this guide, we’ll walk through a practical approach to implementing RTL support using i18next for localization, AsyncStorage for saving the user’s selected language, and I18nManager for controlling layout direction. We’ll also cover platform-specific behavior, including the iOS restart issue and a patch-based workaround for older React Native versions. :contentReference[oaicite:1]{index=1}

Step 1: Setting Up Translations

Start by organizing translations using JSON files. Two sample files might look like this:

translations/en.json

{
  "welcome": "Welcome",
  "change_language": "Change Language",
  "hello": "Hello"
}

translations/ar.json

{
  "welcome": "مرحبا",
  "change_language": "تغيير اللغة",
  "hello": "مرحبا"
}

Step 2: Initializing i18next with React Native

To handle localization, i18next and react-i18next are configured alongside AsyncStorage to persist the selected language. Here's the implementation, broken into logical chunks:

Import necessary modules

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { I18nManager } from 'react-native';
import en from './translations/en.json';
import ar from './translations/ar.json';

Define translation resources

const resources = {
  en: { translation: en },
  ar: { translation: ar },
};

Retrieve stored language preference (or default based on layout direction)

const getStoredLanguage = async () => {
  const stored = await AsyncStorage.getItem('appLanguage');
  if (stored) return stored;
  return I18nManager.isRTL ? 'ar' : 'en';
};

Initialize i18n after fetching the preferred language

const initI18n = async () => {
  const language = await getStoredLanguage();

  await i18n.use(initReactI18next).init({
    resources,
    lng: language,
    fallbackLng: 'en',
    interpolation: {
      escapeValue: false,
    },
  });

  const isRTL = language === 'ar';
  I18nManager.allowRTL(isRTL);
  I18nManager.forceRTL(isRTL);
};

initI18n();

export default i18n;

Important: Import this i18n setup file in your app's root layout before rendering the rest of your application. This guarantees that translations and layout direction are initialized before any UI is displayed.

Step 3: Switching Languages Dynamically

To allow users to change language at runtime and reflect RTL changes in the layout, the following function handles the switch:

import { I18nManager } from 'react-native';
import RNRestart from 'react-native-restart';
import AsyncStorage from '@react-native-async-storage/async-storage';
import i18n from './i18n';

const switchLanguage = async (selectedLanguage) => {
  const isRTL = selectedLanguage === 'ar';

  await AsyncStorage.setItem('appLanguage', selectedLanguage);
  await i18n.changeLanguage(selectedLanguage);

  I18nManager.forceRTL(isRTL);
  RNRestart.Restart();
};
  • selectedLanguage is a regular useState variable used to track the language the user wants to switch to.
  • I18nManager.forceRTL() is used to change the layout direction.
  • After making this change, RNRestart.Restart() is called to restart the app, ensuring that the layout updates are applied immediately.

Step 4: Handling Platform-Specific RTL Behavior

React Native applies RTL layout changes differently across platforms:

  • Android: Works correctly after a single reload using RNRestart.
  • iOS: Requires a full app restart and does not reflect changes even after reloads in versions prior to 0.79.0.

This behavior was addressed in React Native 0.79.0, where layout context updates dynamically. For projects using earlier versions, manual patching is necessary.

GitHub issue reference: https://github.com/facebook/react-native/pull/49455

Step 5: Supporting RTL in iOS for Versions Below 0.79.0

To enable RTL on iOS without upgrading React Native, a patch can be applied:

Install patch-package

npm install patch-package
# or
yarn add patch-package

Modify internal RN layout handling

Navigate to:

node_modules/react-native/Libraries/Utilities/I18nManager.js

Locate this line:

forceRTL: (shouldBeRTL: boolean): void => {

Add the following line immediately after the opening brace:

NativeI18nManager?.doLeftAndRightSwapInRTL(shouldBeRTL);

Generate the patch

npx patch-package react-native

Ensure patch is applied on every install

Update package.json:

{
  "scripts": {
    "postinstall": "patch-package"
  }
}

This ensures the patch persists across installs and CI/CD pipelines.

Step 6: RTL-Aware Styling Guidelines

To build components that adapt seamlessly between LTR and RTL:

Use logical padding/margin properties

const styles = StyleSheet.create({
  container: {
    paddingStart: 16,  // instead of paddingLeft
    paddingEnd: 16,    // instead of paddingRight
    marginStart: 8,
    marginEnd: 8,
  },
});

Flip layout direction conditionally

import { I18nManager } from 'react-native';

const isRTL = I18nManager.isRTL;

const rowStyle = {
  flexDirection: isRTL ? 'row-reverse' : 'row',
};

Align text appropriately

const textStyle = {
  textAlign: isRTL ? 'right' : 'left',
  writingDirection: isRTL ? 'rtl' : 'ltr',
};

These styling practices make the UI adaptive and prevent hardcoded visual inconsistencies.

Final Notes

Right-to-left support in React Native Expo can be achieved smoothly using a combination of i18next, persistent storage, I18nManager, and platform-specific fixes. By structuring the localization setup clearly and applying conditional logic for layout direction, applications can offer a rich, multilingual experience without disrupting the user journey, even in legacy environments.

Originally published on GeekyAnts Blog

DE
Source

This article was originally published by DEV Community and written by GeekyAnts Inc.

Read original article on DEV Community
Back to Discover

Reading List