RTL support
Build right-to-left interfaces with Untitled UI React. Supports Arabic, Hebrew, Persian, and other RTL languages using CSS logical properties.
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 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 4 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
Set the dir attribute
Add dir="rtl" to your <html> element or any container:
<html lang="ar" dir="rtl">
For dynamic direction switching, use React state:
<html lang={locale} dir={direction}>
Add I18nProvider for React Aria
React Aria components (modals, popovers, tooltips) render in portals outside your DOM tree. They need I18nProvider to inherit the correct direction:
import { I18nProvider } from "react-aria"; function App({ children }) { return ( <I18nProvider locale="ar"> {children} </I18nProvider> ); }
This ensures portal-based components render with the correct text direction and locale.
Migrate classes with the CLI
Use our CLI to automatically convert physical direction classes to logical properties across your project:
npx untitledui@latest migrate rtl --dry-run npx untitledui@latest migrate rtl
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 rtl 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 |
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 |
| 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 |
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:
[data-rtl-flip]:dir(rtl) { transform: scaleX(-1); }
The :dir(rtl) 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
import { ArrowNext, ChevronPrevious } from "@untitledui/icons"; // Flips automatically in RTL — no extra props or classes needed <Button iconTrailing={ArrowNext}>Next</Button> <Button iconLeading={ChevronPrevious}>Previous</Button>
Using a different icon library
If you're not using @untitledui/icons, you can flip icons manually with Tailwind's rtl: variant:
<ArrowRight className="rtl:-scale-x-100" />
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:
- Sets
dir="rtl"on the story canvas - Switches React Aria's
I18nProviderto Arabic locale - All components mirror automatically
This is configured in .storybook/wrapper.tsx using the storybook-addon-rtl package.
FAQs
Please refer to our frequently asked questions page for more.
Only if you want RTL support. The logical equivalents (ms-, ps-) render identically in LTR mode, so migrating is safe and backwards-compatible. Use npx untitledui@latest migrate rtl --dry-run to preview what changes.
Symmetrical utilities like px-4, mx-4, p-4, and m-4 apply to both sides and work correctly in RTL without any changes. Only single-side utilities (ml-, mr-, pl-, pr-) need migration.
No. Logical properties like ms-4 produce the exact same CSS as ml-4 in LTR mode (margin-inline-start: 1rem = margin-left: 1rem when dir="ltr"). The only difference is in RTL mode.
Slide animations like slide-in-from-right need an rtl: variant (rtl:slide-in-from-left) because CSS transforms don't have logical equivalents. The CLI adds these automatically.
Yes. React Aria has built-in RTL support. Wrap your app with and all React Aria components (modals, popovers, date pickers, etc.) will render correctly in RTL.
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.