RTLRight-to-LeftArabicHebrewReactCSSi18n

RTL Language Support in React Applications

Implement Right-to-Left (RTL) language support in React SaaS apps — layout mirroring, CSS logical properties, third-party component compatibility, and testing.

Taylor Brooks
Taylor Brooks
June 1, 202614 min read
Also available in:Deutsch · 中文

TL;DR: Supporting Arabic, Hebrew, Persian, and Urdu requires layout mirroring. This guide covers CSS logical properties, dir="rtl" handling, third-party component adaptation, and testing RTL layouts. RTL patterns from tanstackship.com.

Introduction

Over 400 million people read from right to left. Supporting RTL languages is essential for Middle Eastern, South Asian, and North African markets. RTL support involves more than text direction — it affects layout, icons, animations, and form interactions. For foundational i18n architecture, see our i18n for SaaS: Architecture Patterns Compared.

RTL Implementation Approaches

ApproachEffortReliabilityMaintainability
CSS logical propertiesLowExcellentHigh
dir attribute + CSS flipLowGoodMedium
CSS-in-JS with RTL pluginMediumGoodMedium
PostCSS RTL pluginMediumVery goodHigh
Manual RTL stylesheetsHighPoorVery low

CSS Logical Properties (Recommended)

css
/* Before (physical properties — RTL breaks these) */
.sidebar {
  margin-left: 16px;
  padding-right: 24px;
  border-left: 1px solid #e5e7eb;
  text-align: left;
}

/* After (logical properties — auto-adapt to RTL) */
.sidebar {
  margin-inline-start: 16px;
  padding-inline-end: 24px;
  border-inline-start: 1px solid #e5e7eb;
  text-align: start;
}
Physical PropertyLogical Equivalent
leftinset-inline-start
rightinset-inline-end
margin-leftmargin-inline-start
margin-rightmargin-inline-end
padding-leftpadding-inline-start
padding-rightpadding-inline-end
border-leftborder-inline-start
border-rightborder-inline-end

React RTL Context

tsx
// RTL provider component
function RTLProvider({ locale, children }: { locale: string; children: React.ReactNode }) {
  const direction = getDirection(locale) // 'rtl' or 'ltr'
  
  useEffect(() => {
    document.documentElement.dir = direction
    document.documentElement.lang = locale
  }, [direction, locale])
  
  return (
    <ThemeProvider theme={{ direction }}>
      {children}
    </ThemeProvider>
  )
}

// Hook for conditional RTL styles
function useRTL() {
  const { i18n } = useTranslation()
  const isRTL = ['ar', 'he', 'fa', 'ur'].includes(i18n.language)
  
  return {
    isRTL,
    direction: isRTL ? 'rtl' : 'ltr',
    // Flip icons and imagery
    fliplcon: (icon: string) => isRTL ? `${icon}-flip` : icon,
    // Adjust animation direction
    slideDirection: isRTL ? 'left' : 'right',
  }
}

Third-Party Component Compatibility

Component LibraryRTL SupportNotes
Radix UIFullAuto-detects from dir attribute
shadcn/uiFullInherits from Radix UI
TanStack TableGoodManual column alignment needed
TanStack FormFullRespects dir attribute
RechartsPartialAxis labels need manual config
TipTap editorFullRTL via direction extension

Testing RTL Layouts

tsx
// Playwright RTL test
test('dashboard renders correctly in Arabic', async ({ page }) => {
  await page.goto('/ar/dashboard')
  
  // Check document direction
  const dir = await page.getAttribute('html', 'dir')
  expect(dir).toBe('rtl')
  
  // Check layout mirroring
  const sidebarBox = await page.locator('[data-testid="sidebar"]').boundingBox()
  const mainBox = await page.locator('[data-testid="main-content"]').boundingBox()
  
  // In RTL, sidebar should be on the right
  expect(sidebarBox.x + sidebarBox.width).toBeCloseTo(mainBox.x, 0)
  
  // Visual snapshot comparison
  await expect(page).toHaveScreenshot('dashboard-ar.png')
})

Conclusion

Start with CSS logical properties — they handle 90% of RTL layout automatically. Add a locale-aware RTL context provider, test with Playwright visual snapshots, and audit third-party components for RTL compatibility. Support the four major RTL languages (Arabic, Hebrew, Persian, Urdu) to access fast-growing markets.

For navigating these markets, see Middle East Market Entry Guide. Locale-aware formatting is covered in Localizing Date, Time, Number, and Currency Formats. For responsive layout patterns, our CSS Container Queries Guide provides modern alternatives.