Skip to main content
React Native Bread supports right-to-left (RTL) layouts for languages like Arabic, Hebrew, Farsi, and Urdu. There are two approaches depending on how you handle RTL in your app.

Native RTL vs code-level RTL

React Native provides two ways to handle RTL:
If you’re using I18nManager.forceRTL(), do not enable the rtl config option. The native RTL behavior will handle everything automatically.
For full RTL support using I18nManager:
1

Enable RTL in your app

Add this code before your app initializes (e.g., in index.js or App.tsx):
index.js
import { AppRegistry, I18nManager } from 'react-native';
import App from './App';

// Force RTL layout
I18nManager.forceRTL(true);
I18nManager.allowRTL(true);

AppRegistry.registerComponent('main', () => App);
2

Add BreadLoaf to your layout

No special configuration needed:
App.tsx
import { BreadLoaf } from 'react-native-bread';

export default function App() {
  return (
    <>
      <NavigationContainer>
        <Stack.Navigator />
      </NavigationContainer>
      <BreadLoaf />
    </>
  );
}
3

Use toasts normally

Toasts will automatically render RTL:
import { toast } from 'react-native-bread';

toast.success('تم الحفظ', 'تم حفظ التغييرات بنجاح');
toast.error('خطأ', 'حدث خطأ ما');
Changing I18nManager.forceRTL() requires a full app restart to take effect. It won’t update when hot reloading.

Code-level RTL setup

If you’re not using I18nManager and handle RTL manually:
1

Detect RTL language

Create a hook or context to detect the current language direction:
import { create } from 'zustand';

type Language = 'en' | 'ar' | 'he';

interface LanguageStore {
  language: Language;
  setLanguage: (lang: Language) => void;
  isRTL: boolean;
}

export const useLanguageStore = create<LanguageStore>((set) => ({
  language: 'en',
  isRTL: false,
  setLanguage: (language) => {
    const isRTL = ['ar', 'he', 'fa', 'ur'].includes(language);
    set({ language, isRTL });
  },
}));
2

Configure BreadLoaf with RTL

Pass the rtl option to BreadLoaf:
app/_layout.tsx
import { BreadLoaf } from 'react-native-bread';
import { useLanguageStore } from '@/store/language';

export default function RootLayout() {
  const isRTL = useLanguageStore((state) => state.isRTL);

  return (
    <>
      <Stack />
      <BreadLoaf config={{ rtl: isRTL }} />
    </>
  );
}
3

Use toasts with RTL text

Toasts will now render with RTL layout:
import { toast } from 'react-native-bread';
import { useLanguageStore } from '@/store/language';

export default function Screen() {
  const language = useLanguageStore((state) => state.language);

  const handleSave = () => {
    if (language === 'ar') {
      toast.success('تم الحفظ', 'تم حفظ التغييرات بنجاح');
    } else {
      toast.success('Saved', 'Changes saved successfully');
    }
  };

  return <Button title="Save" onPress={handleSave} />;
}

Visual differences

When RTL is enabled, toasts display:
ElementLTR (default)RTL
Icon positionLeftRight
Text alignmentLeftRight
Close buttonRightLeft
Content orderIcon → Text → CloseClose → Text → Icon
Swipe directionRight to dismissLeft to dismiss

Dynamic RTL switching

With I18nManager (requires restart)

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

const switchToRTL = () => {
  if (!I18nManager.isRTL) {
    I18nManager.forceRTL(true);
    Alert.alert(
      'RTL Enabled',
      'App will restart to apply changes',
      [
        {
          text: 'Restart Now',
          onPress: () => RNRestart.Restart(),
        },
      ]
    );
  }
};

With code-level RTL (dynamic)

import { BreadLoaf } from 'react-native-bread';
import { useLanguageStore } from '@/store/language';
import { useEffect } from 'react';

export default function RootLayout() {
  const { isRTL, setLanguage } = useLanguageStore();

  useEffect(() => {
    // Detect device language on mount
    const deviceLanguage = getDeviceLanguage();
    setLanguage(deviceLanguage);
  }, []);

  return (
    <>
      <Stack />
      <BreadLoaf config={{ rtl: isRTL }} />
    </>
  );
}

RTL with custom toasts

When using custom toasts, you’re responsible for RTL layout in your custom content:
import { toast } from 'react-native-bread';
import { View, Text, I18nManager } from 'react-native';

const showCustomRTLToast = () => {
  const isRTL = I18nManager.isRTL;

  toast.custom(
    <View
      style={{
        flexDirection: isRTL ? 'row-reverse' : 'row',
        padding: 16,
        backgroundColor: '#fff',
        borderRadius: 12,
        gap: 12,
      }}
    >
      <Image source={icon} style={{ width: 40, height: 40 }} />
      <View style={{ flex: 1 }}>
        <Text style={{ textAlign: isRTL ? 'right' : 'left' }}>
          {isRTL ? 'رسالة مخصصة' : 'Custom message'}
        </Text>
      </View>
    </View>
  );
};

Complete RTL example

Here’s a full example with language switching:
store/language.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

type Language = 'en' | 'ar';

interface LanguageStore {
  language: Language;
  isRTL: boolean;
  setLanguage: (lang: Language) => void;
}

export const useLanguageStore = create<LanguageStore>()()
  persist(
    (set) => ({
      language: 'en',
      isRTL: false,
      setLanguage: (language) => {
        const isRTL = language === 'ar';
        set({ language, isRTL });
      },
    }),
    { name: 'language-storage' }
  )
);
app/_layout.tsx
import { Stack } from 'expo-router';
import { BreadLoaf } from 'react-native-bread';
import { useLanguageStore } from '@/store/language';

export default function RootLayout() {
  const isRTL = useLanguageStore((state) => state.isRTL);

  return (
    <>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      </Stack>
      <BreadLoaf
        config={{
          rtl: isRTL,
          position: 'top',
        }}
      />
    </>
  );
}
app/(tabs)/settings.tsx
import { View, Text, Button, StyleSheet } from 'react-native';
import { toast } from 'react-native-bread';
import { useLanguageStore } from '@/store/language';

const translations = {
  en: {
    title: 'Settings',
    switchToArabic: 'Switch to Arabic',
    switchToEnglish: 'Switch to English',
    languageChanged: 'Language changed',
    languageChangedDesc: 'The app is now in',
  },
  ar: {
    title: 'الإعدادات',
    switchToArabic: 'التبديل إلى العربية',
    switchToEnglish: 'التبديل إلى الإنجليزية',
    languageChanged: 'تم تغيير اللغة',
    languageChangedDesc: 'التطبيق الآن باللغة',
  },
};

export default function SettingsScreen() {
  const { language, setLanguage } = useLanguageStore();
  const t = translations[language];

  const handleLanguageSwitch = (newLang: 'en' | 'ar') => {
    setLanguage(newLang);
    const newT = translations[newLang];
    toast.success(
      newT.languageChanged,
      `${newT.languageChangedDesc} ${newLang === 'ar' ? 'العربية' : 'English'}`
    );
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>{t.title}</Text>
      <Button
        title={t.switchToArabic}
        onPress={() => handleLanguageSwitch('ar')}
        disabled={language === 'ar'}
      />
      <Button
        title={t.switchToEnglish}
        onPress={() => handleLanguageSwitch('en')}
        disabled={language === 'en'}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    gap: 12,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 16,
  },
});

Best practices

Use native RTL when possible

I18nManager.forceRTL() provides the most consistent RTL experience and requires no configuration.

Test with real RTL text

Arabic and Hebrew text behaves differently than English. Always test with actual RTL content.

Consider text direction

RTL languages align text to the right. Ensure your custom toasts handle text alignment properly.

Handle mixed content

If you have mixed LTR/RTL content (e.g., English names in Arabic text), use Unicode direction markers.

Troubleshooting

If using I18nManager.forceRTL(), make sure you don’t set rtl: true in the config. Native RTL and code-level RTL should not be used together.If using code-level RTL, verify that rtl: true is being passed to <BreadLoaf />.
RTL text should align to the right. If it’s still left-aligned, you may need to explicitly set text direction:
toast.success('مرحبا', {
  titleStyle: { textAlign: 'right' },
  descriptionStyle: { textAlign: 'right' },
});
If the icon is not flipping, check:
  1. Are you using native RTL (I18nManager.forceRTL())? If yes, don’t set rtl: true in config.
  2. Are you using code-level RTL? If yes, ensure rtl: true is in your BreadLoaf config.
Custom toasts require manual RTL handling in your component:
import { I18nManager } from 'react-native';

toast.custom(
  <View style={{ flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row' }}>
    {/* Your content */}
  </View>
);

API reference

RTL configuration

interface ToastConfig {
  /**
   * Enable right-to-left layout at the code level.
   * 
   * Only needed when handling RTL in JavaScript.
   * If using native RTL (I18nManager.forceRTL), this option is unnecessary
   * as the entire layout flips automatically.
   * 
   * @default false
   */
  rtl?: boolean;
}

Using with I18nManager

import { I18nManager } from 'react-native';
import { BreadLoaf } from 'react-native-bread';

// Enable native RTL (in index.js, before app starts)
I18nManager.forceRTL(true);
I18nManager.allowRTL(true);

// No rtl config needed
export default function App() {
  return (
    <>
      <Stack />
      <BreadLoaf /> {/* Automatically RTL */}
    </>
  );
}

Code-level RTL

import { BreadLoaf } from 'react-native-bread';

export default function App() {
  const isRTL = useRTLDetection(); // Your logic

  return (
    <>
      <Stack />
      <BreadLoaf config={{ rtl: isRTL }} />
    </>
  );
}

Build docs developers (and LLMs) love