DEV Community

Fuqiao Xue
Fuqiao Xue

Posted on

How to Refactor Your React Components for RTL Languages

Internationalizing a React application is not just a matter of translating strings. For Arabic, Hebrew, Persian, Urdu, and other languages written with right-to-left scripts, the interface itself must respect directionality: text flow, alignment, icons, spacing, table order, form behavior, and mixed-language content all need attention.

The W3C Internationalization guidance is clear on the foundation: use HTML directionality to establish the base direction. The dir attribute is the semantic mechanism browsers use to apply the Unicode Bidirectional Algorithm correctly, and W3C recommends adding dir="rtl" to the html element when the overall document direction is right-to-left.

Start with document direction

A common React anti-pattern is to scatter conditional class names across components:

<div className={locale === "ar" ? "text-right flex-row-reverse" : "text-left"}>
  ...
</div>
Enter fullscreen mode Exit fullscreen mode

This is brittle and repetitive. Instead, set dir and lang at the document or app shell level:

const rtlLocales = new Set(["ar", "he", "fa", "ur"]);

export function AppShell({ locale, children }) {
  const dir = rtlLocales.has(locale) ? "rtl" : "ltr";

  return (
    <html lang={locale} dir={dir}>
      <body>{children}</body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

In client-rendered apps where you cannot render <html> directly, update it from your root layout:

import { useEffect } from "react";

const rtlLocales = new Set(["ar", "he", "fa", "ur"]);

export function DirectionProvider({ locale, children }) {
  useEffect(() => {
    document.documentElement.lang = locale;
    document.documentElement.dir = rtlLocales.has(locale) ? "rtl" : "ltr";
  }, [locale]);

  return children;
}
Enter fullscreen mode Exit fullscreen mode

Setting direction at the html level influences paragraph alignment, punctuation placement, table column progression, form-field behavior, overflow direction, and CSS mirroring when logical properties are used.

Replace left/right CSS with logical properties

Refactoring for RTL should not mean duplicating every stylesheet. The better approach is to remove physical-direction assumptions.

Instead of this:

.card {
  margin-left: 1rem;
  padding-right: 1.5rem;
  border-left: 4px solid currentColor;
  text-align: left;
}
Enter fullscreen mode Exit fullscreen mode

Use logical properties:

.card {
  margin-inline-start: 1rem;
  padding-inline-end: 1.5rem;
  border-inline-start: 4px solid currentColor;
  text-align: start;
}
Enter fullscreen mode Exit fullscreen mode

In LTR, inline-start maps to left. In RTL, it maps to right. That means the same component works in both directions.

Useful replacements:

/* Before */
margin-left: 16px;
margin-right: 8px;
padding-left: 12px;
border-right: 1px solid #ccc;
left: 0;
right: auto;
text-align: left;

/* After */
margin-inline-start: 16px;
margin-inline-end: 8px;
padding-inline-start: 12px;
border-inline-end: 1px solid #ccc;
inset-inline-start: 0;
inset-inline-end: auto;
text-align: start;
Enter fullscreen mode Exit fullscreen mode

For React components using utility classes, create direction-safe abstractions:

function Card({ children }) {
  return (
    <section className="rounded-lg border p-4 text-start">
      {children}
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

Prefer utilities such as text-start, text-end, ms-*, me-*, ps-*, and pe-* when your CSS framework supports them.

Refactor layout intent, not just visual direction

Not everything should mirror.

Navigation, chat bubbles, disclosure arrows, pagination, timelines, and media controls often have different rules. Some reflect text direction; others represent time, physical movement, or brand identity.

Ask this for every left/right decision:

Is this position relative to reading direction, or is it physically fixed?

Use dir="auto" for user-generated content

User-generated content is often multilingual. A Hebrew comment may appear in an English UI. An English product name may appear inside an Arabic page. You often do not know the direction ahead of time.

Use dir="auto" on content containers whose text comes from users, APIs, search results, product catalogs, or databases:

function Comment({ author, text }) {
  return (
    <article className="comment">
      <strong dir="auto">{author}</strong>
      <p dir="auto">{text}</p>
    </article>
  );
}
Enter fullscreen mode Exit fullscreen mode

W3C recommends dir="auto" for forms and inserted text when the direction of runtime content is unknown. The browser determines direction from the first strong directional character.

Isolate inline bidirectional text

When you know the direction of an inline phrase, wrap it tightly and set dir:

<p>
  The title is{" "}
  <cite dir="rtl">
    مدخل إلى <span dir="ltr">C++</span>
  </cite>{" "}
  in Arabic.
</p>
Enter fullscreen mode Exit fullscreen mode

A small React helper can make this habit consistent:

export function BidiText({ as: Component = "span", children, dir = "auto" }) {
  return <Component dir={dir}>{children}</Component>;
}
Enter fullscreen mode Exit fullscreen mode

Usage:

<BidiText as="cite">{bookTitle}</BidiText>
<BidiText>{fileName}</BidiText>
<BidiText>{userDisplayName}</BidiText>
Enter fullscreen mode Exit fullscreen mode

Fix forms deliberately

Use dir="auto" for fields that may contain either LTR or RTL content:

<label>
  {t("bookTitle")}
  <input name="title" dir="auto" />
</label>
Enter fullscreen mode Exit fullscreen mode

For multi-paragraph text:

<textarea name="comment" dir="auto" />
Enter fullscreen mode Exit fullscreen mode

For textarea, dir="auto" can assign direction paragraph by paragraph.

If your server needs to preserve the direction chosen or detected in a form field, HTML also supports dirname:

<input name="comment" dir="auto" dirname="comment.dir" />
Enter fullscreen mode Exit fullscreen mode

That allows the submitted form data to include the computed direction.

Style by language when typography requires it

Some scripts need different fonts, line heights, or emphasis behavior. Do not solve this with locale-specific component forks. Use language-aware CSS.

:lang(ar) {
  font-family: "Noto Naskh Arabic", serif;
  line-height: 1.8;
}

:lang(he) {
  font-family: "Noto Sans Hebrew", sans-serif;
}
Enter fullscreen mode Exit fullscreen mode

W3C recommends the CSS :lang() selector for styling content by language, especially because it recognizes inherited language values rather than requiring every nested element to repeat a lang attribute.

Conclusion

That approach follows the web platform, and it produces React components that are easier to maintain, easier to localize, and much more reliable for the people who use RTL languages every day.

Top comments (0)