DEV Community

Cover image for Implementing RTL (Right-to-Left) in React Native Expo - A Step-by-Step Guide
GeekyAnts Inc
GeekyAnts Inc

Posted on

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"
}
Enter fullscreen mode Exit fullscreen mode

translations/ar.json

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

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';
Enter fullscreen mode Exit fullscreen mode

Define translation resources

const resources = {
  en: { translation: en },
  ar: { translation: ar },
};
Enter fullscreen mode Exit fullscreen mode

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';
};
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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();
};
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

Modify internal RN layout handling

Navigate to:

node_modules/react-native/Libraries/Utilities/I18nManager.js
Enter fullscreen mode Exit fullscreen mode

Locate this line:

forceRTL: (shouldBeRTL: boolean): void => {
Enter fullscreen mode Exit fullscreen mode

Add the following line immediately after the opening brace:

NativeI18nManager?.doLeftAndRightSwapInRTL(shouldBeRTL);
Enter fullscreen mode Exit fullscreen mode

Generate the patch

npx patch-package react-native
Enter fullscreen mode Exit fullscreen mode

Ensure patch is applied on every install

Update package.json:

{
  "scripts": {
    "postinstall": "patch-package"
  }
}
Enter fullscreen mode Exit fullscreen mode

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,
  },
});
Enter fullscreen mode Exit fullscreen mode

Flip layout direction conditionally

import { I18nManager } from 'react-native';

const isRTL = I18nManager.isRTL;

const rowStyle = {
  flexDirection: isRTL ? 'row-reverse' : 'row',
};
Enter fullscreen mode Exit fullscreen mode

Align text appropriately

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

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

Top comments (0)