<header class="flex items-center">

<NuxtImg src="logo.png" class="w-18" alt="Dywan Dev - Digital tools & templates" format="webp" quality="80" loading="lazy" />

<Item><nuxt-link href="/">Home</nuxt-link></Item>

<Item><nuxt-link href="/about">About</nuxt-link></Item>

<Item><nuxt-link href="/works"> Portfolio</nuxt-link></Item>

<Item><nuxt-link href="/contact_me"> Contact</nuxt-link></Item>

<a href="/contact-me" title="Get in touch with Dywan Dev"> Contact </a>

<themeswitch class="moon">Dark/Light</themeswitch>

<langswitch class="en">en/fa</langswitch>

</header>

<initilizecontent class="content">Hello World - Dywan Dev</initilizecontent>

<myname is="Dywan Dev" />

<jobtitle is="Digital tools · Templates · Case studies" />

<portfolios are="ready" number="8" />

<experience years="more than 6" />

<birthdate year="1998" month="july" day="11" />

<skills :list="['NUXT', 'Vue', 'React', 'Next', 'Javascript','Responsive Design', 'i18n', 'TypeScript]" />

<contactDetails :list="['contact@dywandev.com', 'linkedin.com/in/dywan-dev', 'github.com/dywan-dev']" />

Dywan Dev
Blog
RTL and LTR Support in React with i18next: A Practical Guide for Bilingual Apps
4m

RTL and LTR Support in React with i18next: A Practical Guide for Bilingual Apps

Introduction

Building a website that works in both Arabic and English sounds simple until you actually try it. Switching a language is easy. Switching an entire layout direction — while keeping animations smooth, images correctly oriented, and UI components behaving naturally — that's where most developers get stuck.

This guide documents exactly how I solved RTL/LTR support in Dywan Dev, a fullstack web platform built with React, Vite, and i18next. No theory. Just what works.

1. Setting Up i18next in a React Vite App

First install the required packages:

npm install i18next react-i18next i18next-browser-languagedetector

Create your translation files:

src/
  locales/
    en/translation.json
    fr/translation.json
    ar/translation.json

Initialize i18next in a dedicated file src/i18n.js:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

import en from './locales/en/translation.json';
import fr from './locales/fr/translation.json';
import ar from './locales/ar/translation.json';

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    resources: { en: { translation: en }, fr: { translation: fr }, ar: { translation: ar } },
    fallbackLng: 'en',
    interpolation: { escapeValue: false }
  });

export default i18n;

Import it once at the top of main.jsx:

import './i18n';

2. Handling Direction Switching — The Right Way

This is where most tutorials stop too early. Setting the language is not enough. You need to update the HTML dir and lang attributes dynamically every time the language changes.

Create a custom hook useDirection.js:

import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';

const RTL_LANGUAGES = ['ar'];

export const useDirection = () => {
  const { i18n } = useTranslation();

  useEffect(() => {
    const isRTL = RTL_LANGUAGES.includes(i18n.language);
    document.documentElement.dir = isRTL ? 'rtl' : 'ltr';
    document.documentElement.lang = i18n.language;
  }, [i18n.language]);
};

Call this hook once in your root App.jsx:

import { useDirection } from './hooks/useDirection';

function App() {
  useDirection();
  return (...);
}

Now your entire layout responds to language changes automatically.

3. The Hard Part — Animations and Images in RTL

This is what nobody writes about. When you switch to RTL, CSS handles text and layout automatically. But animations and images don't always follow.

Animations

If you use Framer Motion, directional animations need to respect the current direction. Instead of hardcoding x: 100, read the direction dynamically:

import { useTranslation } from 'react-i18next';

const { i18n } = useTranslation();
const isRTL = i18n.language === 'ar';

const slideVariant = {
  hidden: { x: isRTL ? -100 : 100, opacity: 0 },
  visible: { x: 0, opacity: 1 }
};

Images

Decorative directional images — arrows, characters facing a direction, UI illustrations — need to mirror in RTL. Use a conditional CSS class:

.mirror-rtl {
  transform: scaleX(-1);
}
<img
  src={arrow}
  className={isRTL ? 'mirror-rtl' : ''}
  alt="direction indicator"
/>

Apply this only to images where direction matters. Not all images need mirroring — only those that imply a direction visually.

4. Tailwind CSS and RTL

Tailwind v3+ has built-in RTL support. Enable it in tailwind.config.js (default content paths are fine); then use ltr: and rtl: variants for directional utilities:

<div class="ltr:pl-4 rtl:pr-4">
  Content
</div>

This keeps your layout clean without writing separate RTL stylesheets.

5. Language Switcher Component

Here's a clean minimal switcher that updates both language and direction:

import { useTranslation } from 'react-i18next';

const languages = [
  { code: 'en', label: 'EN' },
  { code: 'fr', label: 'FR' },
  { code: 'ar', label: 'ع' }
];

export const LanguageSwitcher = () => {
  const { i18n } = useTranslation();

  return (
    <div className="flex gap-2">
      {languages.map(({ code, label }) => (
        <button
          key={code}
          onClick={() => i18n.changeLanguage(code)}
          className={
            i18n.language === code
              ? 'px-3 py-1 rounded bg-primary text-white'
              : 'px-3 py-1 rounded bg-transparent'
          }
        >
          {label}
        </button>
      ))}
    </div>
  );
};

Conclusion

RTL support is not just a translation problem — it's a layout, animation, and UX problem. The combination of i18next for content, a direction hook for HTML attributes, and conditional logic for animations covers 95% of real-world cases.

If you're building for Arabic-speaking markets — or any bilingual audience — getting this right from the start saves you significant refactoring later.

Dywan Dev is built on this exact architecture — a multilingual, RTL-ready platform engineered for global reach. If you need a website that speaks your audience's language — literally — get in touch.

Continue Reading

View allView all
WhatsApp