---
title: RTL Support
version: 1.1+
description:
  Chakra UI provides built-in support right-to-left (RTL) languages like arabic
  and hebrew, out of the box.
category: 'features'
---

For right-to-left (RTL) languages such as Arabic and Hebrew to be semantically
appropriate, in addition to translating the texts, you need to also mirror the
layout.

Chakra UI makes it possible to support RTL languages and LTR languages in the
same app. There are 2 methods of adding RTL support:

- Using the RTL Stylis Plugin
- Using RTL-aware style props

## Using RTL Stylis Plugin

Since Chakra UI is built on top of `@emotion/react`, you can leverage stylis
plugins like `stylis-plugin-rtl` to automatically transform the generated styles
to their RTL equivalent.

Here's how to set it up.

1. Install the stylis plugin and emotion's cache

<PackageManagers
  command={{
    npm: 'npm i stylis stylis-plugin-rtl @emotion/cache',
    yarn: 'yarn add stylis stylis-plugin-rtl @emotion/cache',
    pnpm: 'pnpm add stylis stylis-plugin-rtl @emotion/cache',
    bun: 'bun add stylis stylis-plugin-rtl @emotion/cache',
  }}
/>

2. Create the RTL provider using `CacheProvider` from emotion

```jsx live=false
// src/components/rtl-provider.js

import { CacheProvider } from '@emotion/react'
import createCache from '@emotion/cache'
import rtl from 'stylis-plugin-rtl'

// NB: A unique `key` is important for it to work!
const options = {
  rtl: { key: 'css-ar', stylisPlugins: [rtl] },
  ltr: { key: 'css-en' },
}

export function RtlProvider({ children }) {
  const { locale } = useRouter()
  const dir = locale == 'ar' ? 'rtl' : 'ltr'
  const cache = createCache(options[dir])
  return <CacheProvider value={cache} children={children} />
}
```

3. Add RTL provider to the application's root

```jsx live=false ln={4,10-12}
// pages/_app.js

import { ChakraProvider } from '@chakra-ui/react'
import { RtlProvider } from '@/components/rtl-provider'

function App(props) {
  const { Component, pageProps } = props
  return (
    <ChakraProvider>
      <RtlProvider>
        <Component {...pageProps} />
      </RtlProvider>
    </ChakraProvider>
  )
}

export default App
```

4. Add the `dir` and `lang` attributes to the `html` tag.

You'll need to make a few changes to your markup. In the `<html>` tag, you'll
need to add a `dir` attribute with a value of `rtl` or `ltr`. Here's what your
`<html>` tag should look like:

```jsx live=false
/**
 * `lang` can be "ar", "en", "he", etc.
 * `dir` can be "rtl" or "ltr"
 */
<html lang='ar' dir='rtl'>
  {/* Content */}
</html>
```

In Next.js, you can achieve this by adding a `pages/_document.js` file and using
this API:

```jsx live=false ln={11-12,15}
// pages/_document.js

import NextDocument, { Html, Head, Main, NextScript } from "next/document"

class Document extends NextDocument {
  static async getInitialProps(ctx) {
    return await NextDocument.getInitialProps(ctx)
  }

  render() {
    const { locale } = this.props.__NEXT_DATA__
    const dir = locale === "ar" ? "rtl" : "ltr"
    return (
      {/* 👇🏻 Here's the place to change the `dir` and `lang` */}
      <Html dir={dir} lang={locale}>
        <Head />
        <body >
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default Document
```

5. Add a way to switch between LTR and RTL

For example, in `Next.js`, you can change the `locale` on the route by
leveraging the built-in `useRouter` hook.

```jsx live=false
// src/components/lang-switcher.js

function LangSwitcher() {
  const { locale, push, reload, pathname } = useRouter()
  const nextLocale = locale === 'en' ? 'ar' : 'en'

  const onClick = async () => {
    await push(pathname, { locale: nextLocale })
    // force a reload for it to work correctly.
    reload()
  }

  return <button onClick={onClick}>Change to {nextLocale}</button>
}

export default LangSwitcher
```

### Caveats of this approach

- You might need to force a reload of the page to get it working correctly.
- You might need to change the placement of components like `Popover`, `Drawer`,
  `Tooltip` to match RTL.
- The need to install extra packages like `stylis`, `stylis-plugin-rtl` might
  increase your final bundle.

## Using RTL-aware style props

This is the recommended way to support RTL in Chakra UI. With this approach we
use the appropriate CSS logical properties, and manage the placements of
components like `Popover`, `Drawer`, `Tooltip` to match RTL.

Here's how to set it up:

1. Add a `direction` key to the theme

You can use the `extendTheme` function or any other preferred approach to add
`direction` key to the theme. Then, add the custom theme to `ChakraProvider`.

> Due to the fact the some CSS logical properties aren't supported in all
> browsers, we flip the styles based on the `direction` as a temporary polyfill.

```jsx live=false
// src/components/chakra-rtl-provider.js

function ChakraRTLProvider({ children }) {
  const { locale } = useRouter()
  const direction = locale === 'ar' ? 'rtl' : 'ltr'

  // 👇🏻 Here's the place we add direction to the theme
  const theme = extendTheme({ direction })

  return <ChakraProvider theme={theme}>{children}</ChakraProvider>
}
```

2. Add the `dir` and `lang` attributes to the `html` tag.

In Next.js, you can achieve this by adding a `pages/_document.js` file and using
this API:

```jsx live=false ln={11-12,15}
// pages/_document.js

import NextDocument, { Html, Head, Main, NextScript } from "next/document"

class Document extends NextDocument {
  static async getInitialProps(ctx) {
    return await NextDocument.getInitialProps(ctx)
  }

  render() {
    const { locale } = this.props.__NEXT_DATA__
    const dir = locale === "ar" ? "rtl" : "ltr"
    return (
      {/* 👇🏻 Here's the place to change the `dir` and `lang` */}
      <Html dir={dir} lang={locale}>
        <Head />
        <body >
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default Document
```

3. Replace style props with their RTL-aware equivalent

To get our internal RTL system working, you need to replace all physical
`*-left` or `*-right` styles (passed as props or in the `sx` prop) to their
bi-directional versions (`*-start` or `*-end`).

For example:

- Replace `paddingLeft` or `pl` prop with `paddingStart` or `ps`
- Replace `marginRight` or `mr` prop with `marginEnd` or `me`
- Replace `borderLeftRadius` with `borderStartRadius`

Here's a list of the RTL-aware style props you can use alongside other
[CSS logical properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties):

| Style prop                                      | Replace with                                    | Description                               |
| ----------------------------------------------- | ----------------------------------------------- | ----------------------------------------- |
| `paddingLeft`, `pl`                             | `paddingStart`, `ps`                            | padding in start direction                |
| `paddingRight`, `pr`                            | `paddingEnd`, `pe`                              | padding in end direction                  |
| `marginLeft`, `ml`                              | `marginStart`, `ms`                             | margin in start direction                 |
| `marginRight`, `mr`                             | `marginEnd`, `me`                               | margin in end direction                   |
| `roundedLeft`, `borderLeftRadius`               | `roundedStart`, `borderStartRadius`             | rounded borders in start direction        |
| `roundedRight`, `borderRightRadius`             | `roundedEnd`, `borderEndRadius`                 | rounded borders in end direction          |
| `roundedTopLeft`, `borderTopLeftRadius`         | `roundedTopStart`, `borderTopStartRadius`       | rounded borders in top start direction    |
| `roundedTopRight`, `borderTopRightRadius`       | `roundedTopEnd`, `borderTopEndRadius`           | rounded borders in top end direction      |
| `roundedBottomLeft`, `borderBottomLeftRadius`   | `roundedBottomStart`, `borderBottomStartRadius` | rounded borders in bottom start direction |
| `roundedBottomRight`, `borderBottomRightRadius` | `roundedBottomEnd`, `borderBottomEndRadius`     | rounded borders in bottom end direction   |
| `borderLeft`                                    | `borderStart`, `borderInlineStart`              | border width in start direction           |
| `borderRight`                                   | `borderEnd`, `borderInlineEnd`                  | border width in end direction             |
| `left`                                          | `insetStart`,                                   | horizontal position in start direction    |
| `right`                                         | `insetEnd`,                                     | horizontal position in end direction      |

4. Add a way to switch between LTR and RTL

For example, in `Next.js`, you can change the `locale` on the route by
leveraging the built-in `useRouter` hook.

```jsx live=false
// src/components/lang-switcher.js

function LangSwitcher() {
  const { locale, push, reload, pathname } = useRouter()
  const nextLocale = locale === 'en' ? 'ar' : 'en'

  const onClick = async () => {
    await push(pathname, { locale: nextLocale })
  }

  return <button onClick={onClick}>Change to {nextLocale}</button>
}

export default LangSwitcher
```

Asides updating the style props you use in your application, we think this is
the best approach. In the end it's up to your team to decide which approach to
go with.

## Additional resources

- Blog Post -
  [Setup an i18n routing with Nextjs and Chakra UI](https://dev.to/ugbechike/setup-an-internationalization-i18n-routing-web-app-with-nextjs-and-chakra-ui-39f1)
- MDN
  [CSS Logical Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties)

> If you'd like to share your RTL setup with the community, feel free to add a
> PR that updates this section.
