Looking around there is a library called react-with-direction from airbnb that provides a DirectionProvider - component you could wrap your components in based on the language. Hope that helps.


If you use flexbox and css grid they have RTL support built in. Then use CSS Logical Properties for margin, padding, border, etc. If that is not enough, then you can use [dir="rtl"] .your-class as a fallback.

Now you don't have two separate css files to maintain.

Here is a cross browser margin-right example.

-webkit-margin-end: 25px;
margin-inline-end: 25px;
@supports (not (-webkit-margin-end: 0)) and (not (margin-inline-end: 0)) {
    margin-right: 25px;

You could wrap that up into a mixin for easier use across your app.


Here is a simple solution that requires ejecting and adding a lightweight webpack-rtl-plugin.

After running

npx create-react-app react-rtl 
cd react-rtl
yarn eject
yarn add -D webpack-rtl-plugin @babel/plugin-transform-react-jsx-source

Go to config/webpack.config.js and make some tweaks:

// import the plugin
const WebpackRTLPlugin = require('webpack-rtl-plugin')

// ...

module: { ... }
plugins: [
   // ...,
   // use the plugin
   new WebpackRTLPlugin({ diffOnly: true })
// ...

On this stage, if you run yarn build and look up build/static/css folder, you should hopefully see additional .rtl.css file that contains your rtl styles. Then we need to tell webpack to use MiniCssExtractPlugin.loader for development as well so it will serve styles through link tags instead of inline styles:

// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
  const loaders = [
    isEnvDevelopment && { loader: MiniCssExtractPlugin.loader }, // <-- use this
    // isEnvDevelopment && require.resolve('style-loader'), <-- instead of this 

and don't forget the plugin, lol:

module: { ... }
plugins: [
   // ...,

   // isEnvProduction &&      <-- comment this out
   new MiniCssExtractPlugin({
     // Options similar to the same options in webpackOptions.output
     // both options are optional
     filename: 'static/css/[name].[contenthash:8].css',
     chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',

   // ...

And from here you can finally grab your default stylesheet href and use to insert rtl styles. Here's how you could implement it:

class RtlCssBundleService {
  constructor() {
    this.rtlApplied = false
    this.rtlStyles = [];
    this.ltrStyles = Array.from(

  insert = () => {
    if (this.rtlApplied) { return }

    this.rtlApplied = true

    if (this.rtlStyles.length) {
      return this.rtlStyles.forEach(style => {

    this.rtlStyles = => {
      const link = document.createElement("link")
      link.href = styleSheet.href.replace(/\.css$/, '.rtl.css')
      link.rel = "stylesheet"
      return link

  detach = () => {
    this.rtlApplied = false
    this.rtlStyles.forEach(style => {

  toggle = () => {
    return this.rtlApplied
      ? this.detach()
      : this.insert()

const rtlStyles = new RtlCssBundleService()

export default rtlStyles

Then use this from any of your components. So anyway, I'm sure I've missed something and maybe that is a terrible approach, but it seems to work and here is the demo

Related Query

More Query from same tag