|
| 1 | +--- |
| 2 | +title: RTL Support |
| 3 | +description: "Build RTL-aware layouts using logical properties and directional modifiers" |
| 4 | +--- |
| 5 | + |
| 6 | +Build RTL (Right-to-Left) aware layouts using React Native's logical properties and directional modifiers (`rtl:`, `ltr:`). This enables proper support for languages like Arabic, Hebrew, and Persian. |
| 7 | + |
| 8 | +## Overview |
| 9 | + |
| 10 | +RTL support in react-native-tailwind consists of two complementary features: |
| 11 | + |
| 12 | +1. **Logical Properties** — Use `ms-*`, `me-*`, `ps-*`, `pe-*`, `start-*`, `end-*`, etc. for styles that auto-flip based on layout direction |
| 13 | +2. **Directional Modifiers** — Use `rtl:` and `ltr:` to apply styles only in specific layout directions |
| 14 | + |
| 15 | +## Logical Properties |
| 16 | + |
| 17 | +Logical properties automatically flip horizontally based on the layout direction. They map to React Native's built-in logical style properties. |
| 18 | + |
| 19 | +### Logical Spacing |
| 20 | + |
| 21 | +```tsx |
| 22 | +// Uses marginStart/marginEnd instead of marginLeft/marginRight |
| 23 | +<View className="ms-4 me-8 ps-2 pe-4"> |
| 24 | + <Text>Auto-flipping margins and padding</Text> |
| 25 | +</View> |
| 26 | +``` |
| 27 | + |
| 28 | +| Tailwind Class | React Native Property | RTL Behavior | |
| 29 | +| -------------- | --------------------- | ---------------------- | |
| 30 | +| `ms-*` | `marginStart` | Left in LTR, Right in RTL | |
| 31 | +| `me-*` | `marginEnd` | Right in LTR, Left in RTL | |
| 32 | +| `ps-*` | `paddingStart` | Left in LTR, Right in RTL | |
| 33 | +| `pe-*` | `paddingEnd` | Right in LTR, Left in RTL | |
| 34 | + |
| 35 | +### Logical Positioning |
| 36 | + |
| 37 | +```tsx |
| 38 | +// Uses start/end instead of left/right for absolute positioning |
| 39 | +<View className="absolute start-0 end-4 top-0"> |
| 40 | + <Text>Positioned element</Text> |
| 41 | +</View> |
| 42 | +``` |
| 43 | + |
| 44 | +| Tailwind Class | React Native Property | RTL Behavior | |
| 45 | +| -------------- | --------------------- | ---------------------- | |
| 46 | +| `start-*` | `start` | Left in LTR, Right in RTL | |
| 47 | +| `end-*` | `end` | Right in LTR, Left in RTL | |
| 48 | +| `inset-s-*` | `start` | Left in LTR, Right in RTL | |
| 49 | +| `inset-e-*` | `end` | Right in LTR, Left in RTL | |
| 50 | + |
| 51 | +### Logical Border Width |
| 52 | + |
| 53 | +```tsx |
| 54 | +// Uses borderStartWidth/borderEndWidth |
| 55 | +<View className="border-s-2 border-e-4 border-gray-300"> |
| 56 | + <Text>Directional borders</Text> |
| 57 | +</View> |
| 58 | +``` |
| 59 | + |
| 60 | +| Tailwind Class | React Native Property | RTL Behavior | |
| 61 | +| -------------- | --------------------- | ---------------------- | |
| 62 | +| `border-s-*` | `borderStartWidth` | Left in LTR, Right in RTL | |
| 63 | +| `border-e-*` | `borderEndWidth` | Right in LTR, Left in RTL | |
| 64 | + |
| 65 | +### Logical Border Radius |
| 66 | + |
| 67 | +```tsx |
| 68 | +// Uses logical corner names |
| 69 | +<View className="rounded-ss-lg rounded-ee-lg bg-blue-500"> |
| 70 | + <Text>Diagonal rounded corners</Text> |
| 71 | +</View> |
| 72 | +``` |
| 73 | + |
| 74 | +| Tailwind Class | React Native Property | Description | |
| 75 | +| --------------- | ------------------------- | -------------------------- | |
| 76 | +| `rounded-s-*` | Top-start + Bottom-start | Start side corners | |
| 77 | +| `rounded-e-*` | Top-end + Bottom-end | End side corners | |
| 78 | +| `rounded-ss-*` | `borderTopStartRadius` | Top-start corner | |
| 79 | +| `rounded-se-*` | `borderTopEndRadius` | Top-end corner | |
| 80 | +| `rounded-es-*` | `borderBottomStartRadius` | Bottom-start corner | |
| 81 | +| `rounded-ee-*` | `borderBottomEndRadius` | Bottom-end corner | |
| 82 | + |
| 83 | +### Logical Text Alignment |
| 84 | + |
| 85 | +React Native doesn't have `textAlign: 'start'/'end'` like CSS, but `text-start` and `text-end` **automatically expand** to directional modifiers for true RTL support: |
| 86 | + |
| 87 | +```tsx |
| 88 | +// These are equivalent - text-start auto-expands! |
| 89 | +<Text className="text-start">Aligned to start</Text> |
| 90 | +<Text className="ltr:text-left rtl:text-right">Aligned to start</Text> |
| 91 | + |
| 92 | +// These are equivalent - text-end auto-expands! |
| 93 | +<Text className="text-end">Aligned to end</Text> |
| 94 | +<Text className="ltr:text-right rtl:text-left">Aligned to end</Text> |
| 95 | +``` |
| 96 | + |
| 97 | +| Tailwind Class | Expands To | RTL Behavior | |
| 98 | +| -------------- | ---------- | ------------ | |
| 99 | +| `text-start` | `ltr:text-left rtl:text-right` | ✅ Flips correctly | |
| 100 | +| `text-end` | `ltr:text-right rtl:text-left` | ✅ Flips correctly | |
| 101 | +| `text-left` | (no expansion) | Static (no flip) | |
| 102 | +| `text-right` | (no expansion) | Static (no flip) | |
| 103 | + |
| 104 | +:::tip[Automatic Expansion] |
| 105 | +Unlike web Tailwind where `text-start`/`text-end` rely on CSS logical properties, react-native-tailwind automatically expands these to `ltr:`/`rtl:` modifiers at compile time. This provides true RTL support with zero runtime overhead. |
| 106 | +::: |
| 107 | + |
| 108 | +## Directional Modifiers |
| 109 | + |
| 110 | +Use `rtl:` and `ltr:` modifiers to apply styles conditionally based on `I18nManager.isRTL`. |
| 111 | + |
| 112 | +### Basic Example |
| 113 | + |
| 114 | +```tsx |
| 115 | +import { View, Text } from "react-native"; |
| 116 | + |
| 117 | +export function DirectionalCard() { |
| 118 | + return ( |
| 119 | + <View className="p-4 rtl:pr-8 ltr:pl-8 bg-white rounded-lg"> |
| 120 | + <Text className="text-base">Content with directional padding</Text> |
| 121 | + </View> |
| 122 | + ); |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +**Transforms to:** |
| 127 | + |
| 128 | +```tsx |
| 129 | +import { I18nManager, StyleSheet } from "react-native"; |
| 130 | + |
| 131 | +const _twIsRTL = I18nManager.isRTL; |
| 132 | + |
| 133 | +<View |
| 134 | + style={[ |
| 135 | + _twStyles._bg_white_p_4_rounded_lg, |
| 136 | + _twIsRTL && _twStyles._rtl_pr_8, |
| 137 | + !_twIsRTL && _twStyles._ltr_pl_8, |
| 138 | + ]} |
| 139 | +> |
| 140 | + <Text style={_twStyles._text_base}>Content with directional padding</Text> |
| 141 | +</View>; |
| 142 | +``` |
| 143 | + |
| 144 | +### Combining RTL/LTR Modifiers |
| 145 | + |
| 146 | +Apply different styles based on layout direction: |
| 147 | + |
| 148 | +```tsx |
| 149 | +// Different icon positioning for RTL vs LTR |
| 150 | +<View className="flex-row items-center rtl:flex-row-reverse"> |
| 151 | + <Icon name="arrow-right" className="rtl:rotate-180" /> |
| 152 | + <Text className="ml-2 rtl:ml-0 rtl:mr-2">Continue</Text> |
| 153 | +</View> |
| 154 | +``` |
| 155 | + |
| 156 | +### With Platform Modifiers |
| 157 | + |
| 158 | +Directional modifiers work alongside platform modifiers: |
| 159 | + |
| 160 | +```tsx |
| 161 | +<View className="p-4 ios:p-6 android:p-8 rtl:pr-8 ltr:pl-8 bg-white"> |
| 162 | + <Text>Platform + directional styles</Text> |
| 163 | +</View> |
| 164 | +``` |
| 165 | + |
| 166 | +### With Color Scheme Modifiers |
| 167 | + |
| 168 | +Combine with dark mode support: |
| 169 | + |
| 170 | +```tsx |
| 171 | +<View className="bg-white dark:bg-gray-900 rtl:border-r-4 ltr:border-l-4 border-blue-500"> |
| 172 | + <Text className="text-gray-900 dark:text-gray-100">Dark mode + RTL aware</Text> |
| 173 | +</View> |
| 174 | +``` |
| 175 | + |
| 176 | +### With State Modifiers |
| 177 | + |
| 178 | +Works with interactive components: |
| 179 | + |
| 180 | +```tsx |
| 181 | +import { Pressable } from "@mgcrea/react-native-tailwind"; |
| 182 | + |
| 183 | +<Pressable className="bg-blue-500 active:bg-blue-700 rtl:rounded-r-lg ltr:rounded-l-lg p-4"> |
| 184 | + <Text className="text-white">Interactive + directional</Text> |
| 185 | +</Pressable>; |
| 186 | +``` |
| 187 | + |
| 188 | +## Using with tw/twStyle |
| 189 | + |
| 190 | +Directional modifiers also work in `tw` tagged templates and `twStyle()` calls: |
| 191 | + |
| 192 | +```tsx |
| 193 | +import { tw } from "@mgcrea/react-native-tailwind"; |
| 194 | + |
| 195 | +function MyComponent() { |
| 196 | + const cardStyles = tw`p-4 rtl:pr-8 ltr:pl-8 bg-white`; |
| 197 | + |
| 198 | + return ( |
| 199 | + <View style={cardStyles.style}> |
| 200 | + <Text>RTL-aware card</Text> |
| 201 | + </View> |
| 202 | + ); |
| 203 | +} |
| 204 | +``` |
| 205 | + |
| 206 | +The generated object includes: |
| 207 | +- `style` — Array with runtime conditionals |
| 208 | +- `rtlStyle` — RTL-specific styles for manual access |
| 209 | +- `ltrStyle` — LTR-specific styles for manual access |
| 210 | + |
| 211 | +## Supported Modifiers |
| 212 | + |
| 213 | +| Modifier | Condition | Description | |
| 214 | +| -------- | --------------------- | ------------------------ | |
| 215 | +| `rtl:` | `I18nManager.isRTL` | Applied in RTL layouts | |
| 216 | +| `ltr:` | `!I18nManager.isRTL` | Applied in LTR layouts | |
| 217 | + |
| 218 | +## Key Features |
| 219 | + |
| 220 | +- ✅ **Zero runtime overhead** — All parsing happens at compile-time |
| 221 | +- ✅ **Native React Native API** — Uses `I18nManager.isRTL` under the hood |
| 222 | +- ✅ **Works on all components** — No special wrappers needed |
| 223 | +- ✅ **Combines with other modifiers** — Platform, color scheme, and state modifiers |
| 224 | +- ✅ **Works with tw/twStyle** — Full support in template literals and function calls |
| 225 | +- ✅ **Type-safe** — Full TypeScript support |
| 226 | + |
| 227 | +## When to Use What |
| 228 | + |
| 229 | +| Scenario | Recommended Approach | |
| 230 | +| -------- | -------------------- | |
| 231 | +| Spacing that should flip | Logical properties (`ms-*`, `me-*`) | |
| 232 | +| Positioning that should flip | Logical properties (`start-*`, `end-*`) | |
| 233 | +| Completely different styles per direction | Directional modifiers (`rtl:`, `ltr:`) | |
| 234 | +| Icons that need rotation in RTL | `rtl:rotate-180` | |
| 235 | +| Flex direction reversal | `rtl:flex-row-reverse` | |
| 236 | + |
| 237 | +## Complete Example |
| 238 | + |
| 239 | +```tsx |
| 240 | +import { View, Text, Image } from "react-native"; |
| 241 | +import { Pressable } from "@mgcrea/react-native-tailwind"; |
| 242 | + |
| 243 | +export function RTLCard({ title, description, imageUrl, onPress }) { |
| 244 | + return ( |
| 245 | + <View className="bg-white rounded-lg overflow-hidden shadow-md"> |
| 246 | + {/* Image with directional margin */} |
| 247 | + <Image |
| 248 | + source={{ uri: imageUrl }} |
| 249 | + className="w-full h-48" |
| 250 | + /> |
| 251 | + |
| 252 | + {/* Content with logical padding */} |
| 253 | + <View className="p-4 ps-6 pe-4"> |
| 254 | + <Text className="text-xl font-bold mb-2 text-start">{title}</Text> |
| 255 | + <Text className="text-base text-gray-600 mb-4 text-start">{description}</Text> |
| 256 | + |
| 257 | + {/* Button with directional rounding */} |
| 258 | + <Pressable |
| 259 | + className="bg-blue-500 active:bg-blue-700 px-6 py-3 items-center |
| 260 | + rtl:rounded-l-full ltr:rounded-r-full" |
| 261 | + onPress={onPress} |
| 262 | + > |
| 263 | + <View className="flex-row items-center rtl:flex-row-reverse"> |
| 264 | + <Text className="text-white font-semibold me-2">Continue</Text> |
| 265 | + <Text className="text-white rtl:rotate-180">→</Text> |
| 266 | + </View> |
| 267 | + </Pressable> |
| 268 | + </View> |
| 269 | + </View> |
| 270 | + ); |
| 271 | +} |
| 272 | +``` |
| 273 | + |
| 274 | +## Testing RTL Layouts |
| 275 | + |
| 276 | +To test RTL layouts in development: |
| 277 | + |
| 278 | +```tsx |
| 279 | +import { I18nManager } from "react-native"; |
| 280 | + |
| 281 | +// Force RTL layout |
| 282 | +I18nManager.forceRTL(true); |
| 283 | + |
| 284 | +// Then reload the app |
| 285 | +``` |
| 286 | + |
| 287 | +Note: Changes to `I18nManager.forceRTL()` require an app restart to take effect. |
| 288 | + |
| 289 | +## What's Next? |
| 290 | + |
| 291 | +- Learn about [Platform Modifiers](/react-native-tailwind/guides/platform-modifiers/) for platform-specific styles |
| 292 | +- Explore [Color Scheme](/react-native-tailwind/guides/color-scheme/) for dark mode support |
| 293 | +- Check out [State Modifiers](/react-native-tailwind/guides/state-modifiers/) for interactive components |
0 commit comments