Rtl

RTL (Right-to-Left) Layout in React Native feat. I18nManager

Our company recently decided to expand into Arabic-speaking markets. As part of this initiative, my coworker and I needed to research and implement RTL (Right-to-Left) support in our React Native app. This post explains the approaches we explored and how we ultimately implemented RTL support.

I18nManager

I18nManager is a built-in utility in React Native that helps developers enable RTL layout behavior at the native level. It provides methods such as:

  • allowRTL()
  • forceRTL()
  • swapLeftAndRightInRTL()
  • and a boolean property isRTL <- This is the most important

However, there’s a key caveat: changes to I18nManager often require a full app restart to take effect. This is because React Native reads the RTL configuration from the native layer only once when the JavaScript runtime initializes.

So as a developer, you’re faced with a choice:

  • Use I18nManager for native-level RTL support (requires restart), or
  • Manage RTL layout behavior entirely on the JS side using global state/context (no restart)

Without Using I18nManager

If you want to support RTL without restarting the app, you can detect the current language and conditionally style components. Here’s how:

Step 1: Determine RTL language

// utils/isRTL.ts
export const isRTL = (locale: string) =>
  ['ar', 'he', 'fa', 'ur'].includes(locale.split('-')[0]);

Step 2: Provide it via context (or state management)

// context/LocaleContext.tsx
import React from 'react';

export const LocaleContext = React.createContext({ isRTL: false });

Step 3: Use dynamic styles based on isRTL

// components/RtlCard.tsx
import React from 'react';
import { View, Text, StyleSheet, Image } from 'react-native';
import { useTranslation } from 'react-i18next';
import { isRTL } from '../utils/isRTL';

const RtlCard = () => {
  const { i18n } = useTranslation();
  const rtl = isRTL(i18n.language);

  return (
    <View style={[styles.card, rtl && styles.cardRTL]}>
      <Image
        source=
        style={[styles.image, rtl && styles.imageRTL]}
      />
      <View style={styles.textContainer}>
        <Text style={[styles.title, rtl && styles.titleRTL]}>
          {rtl ? 'مرحبا بك' : 'Welcome'}
        </Text>
        <Text style={[styles.description, rtl && styles.descriptionRTL]}>
          {rtl
            ? 'هذا النص يتم عرضه من اليمين إلى اليسار.'
            : 'This text is shown left to right.'}
        </Text>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  card: {
    flexDirection: 'row',
    padding: 16,
    backgroundColor: '#fff',
    borderRadius: 12,
    margin: 16,
    alignItems: 'center',
  },
  cardRTL: {
    flexDirection: 'row-reverse',
  },
  image: {
    width: 50,
    height: 50,
    marginRight: 12,
  },
  imageRTL: {
    marginRight: 0,
    marginLeft: 12,
  },
  textContainer: {
    flex: 1,
  },
  title: {
    fontSize: 18,
    textAlign: 'left',
  },
  titleRTL: {
    textAlign: 'right',
  },
  description: {
    fontSize: 14,
    color: '#666',
    textAlign: 'left',
  },
  descriptionRTL: {
    textAlign: 'right',
  },
});

Step 4: Update layout when language changes

To reflect layout changes dynamically, you can re-render the entire app using a key prop based on language:

// App.tsx
const App = () => {
  const { language } = useTranslation();
  return <MainApp key={language} />;
};

✅ This method does not require restarting the app and is highly customizable.


🧰 Approach 2: Using I18nManager (Native-level RTL)

If you don’t want to manually conditionally style every component, you can use I18nManager for full RTL support from the native side.

However, there’s a caveat:

I18nManager changes (like calling forceRTL(true)) are only read during the initial launch of the JS runtime. They do not automatically trigger layout updates.

This means you must manually restart the app after toggling RTL using something like:

import { I18nManager } from 'react-native';
import RNRestart from 'react-native-restart';

I18nManager.allowRTL(true);
I18nManager.forceRTL(true);
RNRestart.Restart();

🔗 react-native-restart is a helpful library for doing this gracefully.


🧾 Summary

Approach Auto Layout Flip Restart Required Custom Styling
I18nManager ✅ Yes ⚠️ Yes ❌ Minimal
Manual (JS-only) ❌ No (you control it) ✅ No ✅ Full control

Choose based on your app’s scale, available time, and team resources. For our case, we chose manual RTL handling with global isRTL logic because it gave us more flexibility and required no app restarts.