What is going wrong
Lovable generates React projects styled with Tailwind CSS. Those projects are written left to right by default, and we found no RTL guidance in Lovable's official docs index when we checked on 5 November 2026. So when you ask for Arabic, you usually get Arabic words inside an English layout.
The typical symptoms: the logo on the left, chevrons pointing the wrong way, cards with uneven spacing, a drawer menu sliding in from the wrong side, and phone numbers whose digits or plus sign jump around. Here is the same component broken and fixed:
Every fix below can be pasted as code if you edit files, or given to Lovable as a prompt. Work in order: direction first, because the others depend on it.
Step 1: set the direction on the document
Open index.html at the project root and set both attributes on the html tag. lang helps screen readers and search engines; dir flips the layout.
<!doctype html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8" />
<title>متجر العود</title>
</head>
<body>
<div id="root"></div>
</body>
</html>If the app switches languages at runtime, set the attributes from React instead, so English pages flip back:
import { useEffect } from "react";
export function useDocumentDirection(lang: "ar" | "en") {
useEffect(() => {
document.documentElement.lang = lang;
document.documentElement.dir = lang === "ar" ? "rtl" : "ltr";
}, [lang]);
}Projects using shadcn/ui components are built on Radix primitives, which read direction from a provider. Wrap the app so dropdowns, sliders and menus open correctly:
import { DirectionProvider } from "@radix-ui/react-direction";
export default function App() {
return (
<DirectionProvider dir="rtl">
<Routes />
</DirectionProvider>
);
}Step 2: replace physical classes with logical ones
Classes like ml-4, pr-6, left-0 and text-left are tied to physical sides, so they stay put when the direction flips. Tailwind (version 3.3 and later) has logical equivalents that follow the reading direction:
| Physical (breaks in RTL) | Logical (works both ways) |
|---|---|
| ml-4 / mr-4 | ms-4 / me-4 |
| pl-6 / pr-6 | ps-6 / pe-6 |
| left-0 / right-0 | start-0 / end-0 |
| text-left / text-right | text-start / text-end |
| rounded-l-lg / rounded-r-lg | rounded-s-lg / rounded-e-lg |
| border-l-4 / border-r-4 | border-s-4 / border-e-4 |
A search across src/ for ml-, mr-, pl-, pr-, left-, right- and text-left finds most offenders. A card before and after:
// Before
<div className="flex items-center pl-6 border-l-4 border-amber-500">
<img src={logo} className="mr-3 h-8" />
<p className="text-left">قهوة مختصة</p>
</div>
// After
<div className="flex items-center ps-6 border-s-4 border-amber-500">
<img src={logo} className="me-3 h-8" />
<p className="text-start">قهوة مختصة</p>
</div>For custom CSS outside Tailwind, use the logical properties directly:
.sidebar {
margin-inline-start: 1rem; /* not margin-left */
padding-inline-end: 1.5rem; /* not padding-right */
inset-inline-start: 0; /* not left: 0 */
border-inline-start: 4px solid var(--accent);
text-align: start; /* not text-align: left */
}Step 3: use rtl: variants where logic is not enough
Some things have no logical class: horizontal transforms, animations, and the gap utilities that rely on margin. Tailwind's rtl: and ltr: variants apply a class only in one direction.
{/* A drawer that slides in from the reading side */}
<aside className="fixed inset-y-0 start-0 w-72 -translate-x-full rtl:translate-x-full data-[open=true]:translate-x-0">
...
</aside>
{/* space-x uses margins; reverse it in RTL */}
<nav className="flex space-x-4 rtl:space-x-reverse">
<a href="/">الرئيسية</a>
<a href="/menu">المنيو</a>
</nav>gap-4 is direction-neutral and usually simpler than space-x-4 plus rtl:space-x-reverse. Prefer gap in flex and grid layouts.
Step 4: mirror only the icons that show direction
Arrows, chevrons, "back" icons and progress indicators should flip. Logos, play buttons, checkmarks, search glasses, clocks and brand icons should not. With lucide-react icons, which Lovable projects commonly use, add a flip only in RTL:
import { ChevronRight, ArrowRight, Check } from "lucide-react";
<button className="inline-flex items-center gap-2">
التالي
<ArrowRight className="h-4 w-4 rtl:-scale-x-100" />
</button>
{/* Breadcrumb separator flips */}
<ChevronRight className="h-4 w-4 rtl:-scale-x-100" />
{/* A checkmark keeps its shape */}
<Check className="h-4 w-4" />Step 5: isolate numbers and English with bdi
When Arabic text contains a price, a phone number with a plus sign, an order code or an English brand, the browser's bidirectional algorithm can move symbols to the wrong end. Wrap these pieces in <bdi>, and use dir="ltr" on inputs that must be typed left to right:
اطلب الآن عبر Apple Pay بسعر 49 ريال (شامل VAT).
اطلب الآن عبر Apple Pay بسعر 49 ريال (شامل VAT).
<p>
رقم الطلب <bdi>#A-1042</bdi> بقيمة <bdi>250 ر.س</bdi>،
للتواصل: <bdi dir="ltr">+966 55 123 4567</bdi>
</p>
<label htmlFor="phone">رقم الجوال</label>
<input id="phone" type="tel" dir="ltr" className="text-end" placeholder="05xxxxxxxx" />
{/* User-generated text of unknown language */}
<p dir="auto">{review.text}</p>Step 6: load an Arabic font
The system fallback rarely suits Arabic. Load a typeface designed for it and give body text more line height:
خطّ عربي واضح يرفع الثقة ويسهّل القراءة على الجوال.
خطّ عربي واضح يرفع الثقة ويسهّل القراءة على الجوال.
/* src/index.css */
@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@400;500;700&display=swap");
:root[dir="rtl"] body {
font-family: "IBM Plex Sans Arabic", system-ui, sans-serif;
line-height: 1.75;
}Then add it to tailwind.config under theme.extend.fontFamily if you want a font-arabic utility.
Prompt to give Lovable instead
If you prefer not to edit files, paste this into Lovable. Be specific, because a vague "make it Arabic" usually only changes text alignment:
Convert the whole app to Arabic right-to-left. Set lang="ar" and dir="rtl" on the html element and wrap the app in Radix DirectionProvider dir="rtl". Replace every ml/mr/pl/pr/left/right/text-left/rounded-l/border-l class with its logical equivalent (ms/me/ps/pe/start/end/text-start/rounded-s/border-s). Add rtl:-scale-x-100 to arrow and chevron icons only. Wrap prices, phone numbers and order codes in bdi. Load IBM Plex Sans Arabic with line-height 1.75.
Afterwards, check the result on a phone width and work through the test in our guide to real RTL support. The Arabic version of this guide is there for Arabic-speaking teammates.
When to choose Uttra instead
These fixes work, and if your project is mostly English with a few Arabic pages, they are all you need. If Arabic is your main language and you would rather not repeat this pass every time a new screen is generated, an Arabic-first builder avoids the problem at the source. See our Lovable alternative for Arabic for a fair side-by-side.