export const title = "RTL support"
export const description = "Build right-to-left interfaces with Untitled UI React. Supports Arabic, Hebrew, Persian, and other RTL languages using CSS logical properties."
import { RtlDemo } from "./rtl-demo";
## Right-to-left support
Untitled UI React supports right-to-left (RTL) layouts for languages like Arabic, Hebrew, and Persian. Our components use [CSS logical properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values) that automatically adapt based on the document direction.
Switch between English and Arabic to see how the layout mirrors automatically:
Notice how the close button, toggle switches, button order, and text alignment all mirror correctly. This is handled by CSS logical properties — no JavaScript direction detection needed.
## How it works
Traditional CSS properties like `margin-left`, `padding-right`, and `left` are **physical** — they always refer to the same side regardless of text direction. CSS logical properties like `margin-inline-start`, `padding-inline-end`, and `inset-inline-start` are **directional** — they adapt to the document's `dir` attribute.
Tailwind CSS v4.3 provides logical property utilities:
| Physical (LTR only) | Logical (LTR + RTL) | CSS property |
|---|---|---|
| `ml-4` | `ms-4` | `margin-inline-start` |
| `mr-4` | `me-4` | `margin-inline-end` |
| `pl-4` | `ps-4` | `padding-inline-start` |
| `pr-4` | `pe-4` | `padding-inline-end` |
| `left-0` | `start-0` | `inset-inline-start` |
| `right-0` | `end-0` | `inset-inline-end` |
| `text-left` | `text-start` | `text-align: start` |
| `text-right` | `text-end` | `text-align: end` |
| `rounded-l-lg` | `rounded-s-lg` | `border-start-start-radius` |
| `rounded-r-lg` | `rounded-e-lg` | `border-start-end-radius` |
| `border-l` | `border-s` | `border-inline-start` |
| `border-r` | `border-e` | `border-inline-end` |
Symmetrical utilities like `px-4`, `mx-4`, `p-4`, and `m-4` apply to both sides and work correctly in RTL without changes.
## Setting up RTL
Add `dir="rtl"` to your `` element or any container:
```tsx
```
For dynamic direction switching, use React state:
```tsx
```
React Aria components (modals, popovers, tooltips) render in portals outside your DOM tree. They need `I18nProvider` to inherit the correct direction:
```tsx
import { I18nProvider } from "react-aria-components";
function App({ children }) {
return (
{children}
);
}
```
This ensures portal-based components render with the correct text direction and locale.
Use our CLI to automatically convert physical direction classes to logical properties across your project:
```bash
npx untitledui@latest migrate --dry-run
npx untitledui@latest migrate
```
```bash
yarn dlx untitledui@latest migrate --dry-run
yarn dlx untitledui@latest migrate
```
```bash
bunx untitledui@latest migrate --dry-run
bunx untitledui@latest migrate
```
The CLI transforms `ml-` to `ms-`, `pr-` to `pe-`, `left-` to `start-`, `rounded-l-` to `rounded-s-`, and more. Run with `--dry-run` first to preview changes.
## What the CLI migrates
The `migrate` command handles these conversions:
### Direct replacements
These render identically in LTR and automatically mirror in RTL:
| Category | Before | After |
|---|---|---|
| Margin left | `ml-*` | `ms-*` |
| Margin right | `mr-*` | `me-*` |
| Padding left | `pl-*` | `ps-*` |
| Padding right | `pr-*` | `pe-*` |
| Position left | `left-*` | `start-*` |
| Position right | `right-*` | `end-*` |
| Border radius left | `rounded-l-*` | `rounded-s-*` |
| Border radius right | `rounded-r-*` | `rounded-e-*` |
| Border radius top-left | `rounded-tl-*` | `rounded-ss-*` |
| Border radius top-right | `rounded-tr-*` | `rounded-se-*` |
| Border radius bottom-left | `rounded-bl-*` | `rounded-es-*` |
| Border radius bottom-right | `rounded-br-*` | `rounded-ee-*` |
| Border left | `border-l` | `border-s` |
| Border right | `border-r` | `border-e` |
| Text align left | `text-left` | `text-start` |
| Text align right | `text-right` | `text-end` |
| Float left | `float-left` | `float-start` |
| Float right | `float-right` | `float-end` |
| Clear left | `clear-left` | `clear-start` |
| Clear right | `clear-right` | `clear-end` |
| Scroll margin left | `scroll-ml-*` | `scroll-ms-*` |
| Scroll margin right | `scroll-mr-*` | `scroll-me-*` |
| Scroll padding left | `scroll-pl-*` | `scroll-ps-*` |
| Scroll padding right | `scroll-pr-*` | `scroll-pe-*` |
### RTL variant additions
These add an `rtl:` counterpart that only activates when `dir="rtl"` is set:
| Category | Before | After |
|---|---|---|
| Slide in from right | `slide-in-from-right` | `slide-in-from-right rtl:slide-in-from-left` |
| Slide in from left | `slide-in-from-left` | `slide-in-from-left rtl:slide-in-from-right` |
| Slide out to right | `slide-out-to-right` | `slide-out-to-right rtl:slide-out-to-left` |
| Slide out to left | `slide-out-to-left` | `slide-out-to-left rtl:slide-out-to-right` |
| Translate X | `translate-x-4` | `translate-x-4 rtl:-translate-x-4` |
| Negative translate X | `-translate-x-4` | `-translate-x-4 rtl:translate-x-4` |
| Space X | `space-x-4` | `space-x-4 rtl:space-x-reverse` |
| Divide X | `divide-x` | `divide-x rtl:divide-x-reverse` |
| Gradient to right | `bg-linear-to-r` | `bg-linear-to-r rtl:bg-linear-to-l` |
| Gradient to left | `bg-linear-to-l` | `bg-linear-to-l rtl:bg-linear-to-r` |
| Legacy gradient to right | `bg-gradient-to-r` | `bg-gradient-to-r rtl:bg-gradient-to-l` |
| Legacy gradient to left | `bg-gradient-to-l` | `bg-gradient-to-l rtl:bg-gradient-to-r` |
**Safe by design**: These changes don't affect LTR layouts. `ms-4` renders identically to `ml-4` in LTR mode — it only differs when `dir="rtl"` is set.
## Directional icons
Icons like arrows and chevrons may need to flip in RTL when they represent navigation direction (e.g., "next" or "previous").
If you're using `@untitledui/icons`, you can import RTL-aware icon variants that flip automatically based on the document direction:
| Physical icon | RTL-aware icon | Use case |
|---|---|---|
| `ArrowRight` | `ArrowNext` | "Next" navigation, "forward" action |
| `ArrowLeft` | `ArrowPrevious` | "Previous" navigation, "back" action |
| `ChevronRight` | `ChevronNext` | Pagination next, submenu indicator |
| `ChevronLeft` | `ChevronPrevious` | Pagination previous, breadcrumb separator |
| `ChevronRightDouble` | `ChevronNextDouble` | Jump to last page |
| `ChevronLeftDouble` | `ChevronPreviousDouble` | Jump to first page |
### Setup
These icons use a `data-rtl-flip` attribute to mark themselves as flippable. To activate the flip, add this one line to your `globals.css`:
```css
[data-rtl-flip]:dir(rtl) { transform: scaleX(-1); }
```
The [`:dir(rtl)`](https://developer.mozilla.org/en-US/docs/Web/CSS/:dir) pseudo-class matches any element inside a `dir="rtl"` context. When the document is LTR, the selector doesn't match and the icon renders normally. When RTL, `scaleX(-1)` mirrors the icon horizontally. No JavaScript, no runtime cost.
### Usage
```tsx
import { ArrowNext, ChevronPrevious } from "@untitledui/icons";
// Flips automatically in RTL — no extra props or classes needed
```
### Using a different icon library
If you're not using `@untitledui/icons`, you can flip icons manually with Tailwind's `rtl:` variant:
```tsx
```
### When NOT to flip
Not all directional icons should flip. Icons representing fixed concepts should keep their original direction — use the physical icon name for those:
- **External links** (`ArrowUpRight`) — always points up-right
- **Play buttons** — always points right
- **Reply/forward** — follows email convention, not text direction
- **Download/upload** — vertical, direction-independent
## Testing in Storybook
Our Storybook setup includes an RTL toggle button in the toolbar. Clicking it:
1. Sets `dir="rtl"` on the story canvas
2. Switches React Aria's `I18nProvider` to Arabic locale
3. All components mirror automatically
This is configured in `.storybook/wrapper.tsx` using the `storybook-addon-rtl` package.
` and all React Aria components (modals, popovers, date pickers, etc.) will render correctly in RTL.",
},
{
question: "Can I use RTL for just part of my page?",
answer: "Yes. Add `dir=\"rtl\"` to any container element. CSS logical properties inherit the direction from the nearest ancestor with a `dir` attribute. Wrap that section with `` for React Aria components within it.",
},
]}
/>