Everything developers and designers need to know about right-to-left languages and how to support them properly.

Karim Benali
Senior frontend developer with 10+ years building RTL-first applications.
Right-to-left (RTL) text support is often treated as an afterthought in web development—a feature to add "someday" when international markets become important. This approach leads to painful retrofitting, broken layouts, and frustrated users.
The reality is that RTL languages are spoken by over 500 million people worldwide. Arabic alone is the 5th most spoken language globally. Persian, Hebrew, Urdu, and Pashto each have millions of speakers who deserve interfaces that work naturally for them.
This guide covers everything you need to know about RTL support: what it means, how it works, and how to implement it properly.
RTL (Right-to-Left) refers to writing systems where text is written and read from right to left. The primary RTL scripts are:
| Script | Languages | Speakers |
|---|---|---|
| Arabic | Arabic, Persian, Urdu, Pashto, Kurdish, Uyghur | ~400+ million |
| Hebrew | Hebrew, Yiddish | ~9 million |
| Syriac | Syriac, Aramaic | ~400,000 |
| Thaana | Dhivehi (Maldives) | ~350,000 |
In RTL scripts, the following flow from right to left:
However, some elements may still be LTR:
This mixing is called bidirectional text or "BiDi," and it has its own complex rules.
When a page is mirrored for RTL:
LTR Layout: RTL Layout:
┌─────────────────────┐ ┌─────────────────────┐
│ Logo [Nav] [Nav] │ │ [Nav] [Nav] Logo │
├─────────────────────┤ ├─────────────────────┤
│ Sidebar │ Content │ │ Content │ Sidebar │
│ │ │ │ │ │
└─────────────────────┘ └─────────────────────┘Elements that typically mirror in RTL:
Some elements should remain unchanged:
Numbers in Arabic text present an interesting case:
The foundation of RTL support is the HTML dir attribute:
<!-- Set direction at document level -->
<html lang="ar" dir="rtl">
<!-- Or on specific elements -->
<div dir="rtl">
محتوى باللغة العربية
</div>Always pair dir with the appropriate lang attribute:
<html lang="ar" dir="rtl"> <!-- Arabic -->
<html lang="fa" dir="rtl"> <!-- Persian -->
<html lang="he" dir="rtl"> <!-- Hebrew -->
<html lang="ur" dir="rtl"> <!-- Urdu -->The lang attribute affects:
CSS can control direction independently:
/* Set direction */
.rtl-content {
direction: rtl;
}
/* Unicode-bidi controls how direction affects content */
.rtl-content {
direction: rtl;
unicode-bidi: embed;
}Values for unicode-bidi:
normal: No special embeddingembed: Element opens an embedding levelbidi-override: Overrides BiDi algorithmisolate: Isolates content from surroundingsisolate-override: Combines isolate and overrideModern CSS offers logical properties that automatically adapt to writing direction:
/* Physical properties (don't adapt) */
.old-way {
margin-left: 20px;
padding-right: 10px;
text-align: left;
float: left;
}
/* Logical properties (adapt to direction) */
.new-way {
margin-inline-start: 20px;
padding-inline-end: 10px;
text-align: start;
float: inline-start;
}Logical property mapping:
| Physical | Logical (Horizontal) |
|---|---|
| left | inline-start |
| right | inline-end |
| top | block-start |
| bottom | block-end |
| margin-left | margin-inline-start |
| margin-right | margin-inline-end |
| padding-left | padding-inline-start |
| padding-right | padding-inline-end |
| border-left | border-inline-start |
| border-right | border-inline-end |
| text-align: left | text-align: start |
| text-align: right | text-align: end |
Flexbox and CSS Grid naturally support RTL:
.container {
display: flex;
flex-direction: row; /* Automatically reverses in RTL */
}
.grid {
display: grid;
grid-template-columns: 200px 1fr; /* Reverses in RTL */
}No additional CSS needed—just set dir="rtl" on the container.
// Get computed direction of an element
const dir = getComputedStyle(element).direction;
// Check document direction
const isRTL = document.documentElement.dir === 'rtl';
// Or check computed style
const isRTL = getComputedStyle(document.documentElement).direction === 'rtl';Arrow key behavior should adapt to direction:
function handleKeydown(event, isRTL) {
const nextKey = isRTL ? 'ArrowLeft' : 'ArrowRight';
const prevKey = isRTL ? 'ArrowRight' : 'ArrowLeft';
if (event.key === nextKey) {
// Move to next item
} else if (event.key === prevKey) {
// Move to previous item
}
}Touch gestures should also respect direction:
function handleSwipe(direction, isRTL) {
// "next" means swipe left in LTR, swipe right in RTL
const isNextSwipe = isRTL
? direction === 'right'
: direction === 'left';
if (isNextSwipe) {
goToNextSlide();
}
}<!-- Menu automatically reverses in RTL -->
<nav dir="rtl">
<ul class="flex gap-4">
<li><a href="/">الرئيسية</a></li>
<li><a href="/about">حول</a></li>
<li><a href="/contact">اتصل بنا</a></li>
</ul>
</nav><form dir="rtl">
<div class="form-group">
<label for="name">الاسم</label>
<input type="text" id="name" />
</div>
<div class="form-group">
<label for="email">البريد الإلكتروني</label>
<input type="email" id="email" dir="ltr" />
</div>
</form>Note: Email fields often need explicit dir="ltr" since email addresses are always LTR.
<div dir="rtl" class="card flex">
<img src="avatar.jpg" alt="صورة المستخدم" />
<div class="content">
<h3>أحمد محمد</h3>
<p>مطور واجهات أمامية</p>
</div>
</div>Most browsers let you toggle direction:
<html> elementdir="rtl" attributeTest with pseudo-RTL text to catch issues without knowing the language:
// Reverse strings for testing
const pseudoRTL = (text) => text.split('').reverse().join('');Plan for RTL early: Retrofitting RTL support is much harder than building it in from the start.
Use logical properties: CSS logical properties (inline-start, inline-end) automatically adapt to writing direction.
Leverage Flexbox/Grid: Modern layout systems handle direction changes automatically.
Not everything mirrors: Some elements (photos, videos, certain icons) should not flip in RTL.
Test with real users: Native RTL speakers will catch issues automated testing misses.
Everything developers and designers need to know about right-to-left languages and how to support them properly.

Karim Benali
Senior frontend developer with 10+ years building RTL-first applications.
Right-to-left (RTL) text support is often treated as an afterthought in web development—a feature to add "someday" when international markets become important. This approach leads to painful retrofitting, broken layouts, and frustrated users.
The reality is that RTL languages are spoken by over 500 million people worldwide. Arabic alone is the 5th most spoken language globally. Persian, Hebrew, Urdu, and Pashto each have millions of speakers who deserve interfaces that work naturally for them.
This guide covers everything you need to know about RTL support: what it means, how it works, and how to implement it properly.
RTL (Right-to-Left) refers to writing systems where text is written and read from right to left. The primary RTL scripts are:
| Script | Languages | Speakers |
|---|---|---|
| Arabic | Arabic, Persian, Urdu, Pashto, Kurdish, Uyghur | ~400+ million |
| Hebrew | Hebrew, Yiddish | ~9 million |
| Syriac | Syriac, Aramaic | ~400,000 |
| Thaana | Dhivehi (Maldives) | ~350,000 |
In RTL scripts, the following flow from right to left:
However, some elements may still be LTR:
This mixing is called bidirectional text or "BiDi," and it has its own complex rules.
When a page is mirrored for RTL:
LTR Layout: RTL Layout:
┌─────────────────────┐ ┌─────────────────────┐
│ Logo [Nav] [Nav] │ │ [Nav] [Nav] Logo │
├─────────────────────┤ ├─────────────────────┤
│ Sidebar │ Content │ │ Content │ Sidebar │
│ │ │ │ │ │
└─────────────────────┘ └─────────────────────┘Elements that typically mirror in RTL:
Some elements should remain unchanged:
Numbers in Arabic text present an interesting case:
The foundation of RTL support is the HTML dir attribute:
<!-- Set direction at document level -->
<html lang="ar" dir="rtl">
<!-- Or on specific elements -->
<div dir="rtl">
محتوى باللغة العربية
</div>Always pair dir with the appropriate lang attribute:
<html lang="ar" dir="rtl"> <!-- Arabic -->
<html lang="fa" dir="rtl"> <!-- Persian -->
<html lang="he" dir="rtl"> <!-- Hebrew -->
<html lang="ur" dir="rtl"> <!-- Urdu -->The lang attribute affects:
CSS can control direction independently:
/* Set direction */
.rtl-content {
direction: rtl;
}
/* Unicode-bidi controls how direction affects content */
.rtl-content {
direction: rtl;
unicode-bidi: embed;
}Values for unicode-bidi:
normal: No special embeddingembed: Element opens an embedding levelbidi-override: Overrides BiDi algorithmisolate: Isolates content from surroundingsisolate-override: Combines isolate and overrideModern CSS offers logical properties that automatically adapt to writing direction:
/* Physical properties (don't adapt) */
.old-way {
margin-left: 20px;
padding-right: 10px;
text-align: left;
float: left;
}
/* Logical properties (adapt to direction) */
.new-way {
margin-inline-start: 20px;
padding-inline-end: 10px;
text-align: start;
float: inline-start;
}Logical property mapping:
| Physical | Logical (Horizontal) |
|---|---|
| left | inline-start |
| right | inline-end |
| top | block-start |
| bottom | block-end |
| margin-left | margin-inline-start |
| margin-right | margin-inline-end |
| padding-left | padding-inline-start |
| padding-right | padding-inline-end |
| border-left | border-inline-start |
| border-right | border-inline-end |
| text-align: left | text-align: start |
| text-align: right | text-align: end |
Flexbox and CSS Grid naturally support RTL:
.container {
display: flex;
flex-direction: row; /* Automatically reverses in RTL */
}
.grid {
display: grid;
grid-template-columns: 200px 1fr; /* Reverses in RTL */
}No additional CSS needed—just set dir="rtl" on the container.
// Get computed direction of an element
const dir = getComputedStyle(element).direction;
// Check document direction
const isRTL = document.documentElement.dir === 'rtl';
// Or check computed style
const isRTL = getComputedStyle(document.documentElement).direction === 'rtl';Arrow key behavior should adapt to direction:
function handleKeydown(event, isRTL) {
const nextKey = isRTL ? 'ArrowLeft' : 'ArrowRight';
const prevKey = isRTL ? 'ArrowRight' : 'ArrowLeft';
if (event.key === nextKey) {
// Move to next item
} else if (event.key === prevKey) {
// Move to previous item
}
}Touch gestures should also respect direction:
function handleSwipe(direction, isRTL) {
// "next" means swipe left in LTR, swipe right in RTL
const isNextSwipe = isRTL
? direction === 'right'
: direction === 'left';
if (isNextSwipe) {
goToNextSlide();
}
}<!-- Menu automatically reverses in RTL -->
<nav dir="rtl">
<ul class="flex gap-4">
<li><a href="/">الرئيسية</a></li>
<li><a href="/about">حول</a></li>
<li><a href="/contact">اتصل بنا</a></li>
</ul>
</nav><form dir="rtl">
<div class="form-group">
<label for="name">الاسم</label>
<input type="text" id="name" />
</div>
<div class="form-group">
<label for="email">البريد الإلكتروني</label>
<input type="email" id="email" dir="ltr" />
</div>
</form>Note: Email fields often need explicit dir="ltr" since email addresses are always LTR.
<div dir="rtl" class="card flex">
<img src="avatar.jpg" alt="صورة المستخدم" />
<div class="content">
<h3>أحمد محمد</h3>
<p>مطور واجهات أمامية</p>
</div>
</div>Most browsers let you toggle direction:
<html> elementdir="rtl" attributeTest with pseudo-RTL text to catch issues without knowing the language:
// Reverse strings for testing
const pseudoRTL = (text) => text.split('').reverse().join('');Plan for RTL early: Retrofitting RTL support is much harder than building it in from the start.
Use logical properties: CSS logical properties (inline-start, inline-end) automatically adapt to writing direction.
Leverage Flexbox/Grid: Modern layout systems handle direction changes automatically.
Not everything mirrors: Some elements (photos, videos, certain icons) should not flip in RTL.
Test with real users: Native RTL speakers will catch issues automated testing misses.