Right to Left (RTL) in React - Developer's Guide

Rating: 4.56 / 5 Based on 18 reviews

Free product design tips
Join our newsletter and get insights that will change your product design perspective
By submitting your email you agree to receive the content requested and to LeanCode's Privacy Policy.
or follow us

Software development for customers across the globe is full of challenges. We must take into account different languages and time zones. One of the issues that we might face is adding languages read from right to left, such as Arabic or Hebrew, especially if our frontend application was developed only with Left to Right languages in mind.

In this article, I will guide you on how to handle right to left text and layout in your web app in React - my experience is based on the Welliba app that I’ve helped to develop as a frontend developer. Here, by the way, you can read the Welliba Case Study.

If you’re interested in introducing RTL in your mobile app read the article on Right to Left in Flutter.

Why is it worth implementing the RTL website version?

Implementing a Right-to-Left version of the website or web app doesn't only refer to translating it into another language. Offering an RTL website version may be important for several reasons:

  1. Improved User Experience: An RTL version of your website can significantly improve the User Experience for users who speak RTL languages. The content and layout of your website will be optimized for the way these users read, making it more intuitive and easier to navigate so they may be more willing to use your product.
  2. Increased Accessibility: Implementing an RTL version of your website is an important step in making it more accessible to a broader audience. By accommodating RTL languages, you make your website/product more inclusive and welcoming to users from diverse linguistic backgrounds.
  3. Enhanced SEO: Optimizing your website for RTL languages can help improve your search engine optimization (SEO) efforts. This is because search engines consider a website's language and directionality when determining its relevance and ranking.
  4. International Expansion: If you are planning to expand your product and its promotion to regions where RTL languages are spoken, implementing an RTL version of your website or web app is essential. This will help you reach a wider audience by making it accessible to potential customers in those regions.

How the browser handles directionality?

The browser uses Unicode Bidirectional Algorithm (also known as the bidi algorithm) to determine the visual representation of text. It relies on the fact that every character in Unicode has directionality related to it. For example, letters from the Arabic language are strongly typed as RTL. Therefore, by default, paragraphs containing only Arabic characters will be displayed from right to left.

Translations might also contain mixed content in terms of directionality. Consider the following text:

RTL in React - directionality

Source: Unicode Bidirectional Algorithm basics

Bidi algorithm produces output taking into account every part of the text. In this case, every English part will be displayed in an LTR manner, and every Arabic part will be displayed in an RTL manner. The order of parts is determined by base direction, which is LTR by default. It can be changed to RTL by using dir attribute. After changing the base direction to RTL the order of parts is reversed, which is natural if the current language of the page is Arabic.

RTL React - bidi algorithm

Source: Unicode Bidirectional Algorithm basics

Directionality in frontend apps

Enabling proper directionality based on the current language

Frontend apps adjusted for RTL languages are not only about displaying texts from right to left, just like the browser does by default. Because of the fact that the pages will be read in that direction, the whole layout should flow from right to left. 

To enable such behavior, the directionality of the application needs to be set dynamically with respect to the current language. This can be done using the dir attribute on the <html> element, which can be set to either "ltr" for left-to-right languages or "rtl" for right-to-left languages. This change results in browsers laying out elements from right to left for RTL languages. This also applies to flexbox and grid layouts, which will have reversed order of elements.

The following HTML code demonstrates how to enable RTL layout when the page is in RTL language.

<!DOCTYPE html>
<html dir="rtl">
 <head>
   <title>My RTL Page</title>
 </head>
 <body>
   <!-- Page content goes here -->
 </body>
</html>

Usually languages are managed by internationalization frameworks. One of the examples is react-18next. Enabling proper directionality based on language direction is performed by setting the dir attribute whenever the current language of the page changes. The following code shows how it can be achieved with simple useEffect.

useEffect(() => {
    const dir = i18n.dir(i18n.language);
    document.documentElement.dir = dir;
 }, [i18n, i18n.language]);

Why doesn't my app look good out of the box?

Unfortunately, if the application was built only for LTR languages, setting dir attribute is not enough. Most likely, the page won’t look good at this point, and the reasons behind it are CSS properties that have left and right included in their names. Properties like left, right, margin-left, margin-right, padding-left, padding-right, border-left, and border-right aren’t “reversed” in RTL mode.

Because of that, absolutely or fixed-positioned elements won’t be placed correctly. The same applies to margins, paddings and borders. When the whole layout is reversed, they are applied to the wrong side.

The layout and design of the web app need to be adjusted to ensure that the content is displayed correctly. This may include adjusting the position of menus, images, and other page elements to accommodate RTL languages.

RTL implementation in Welliba web app

Writing styles with RTL in mind

To avoid such problems, the CSS properties mentioned above should be avoided. To space elements inside a flexbox or grid container, it’s a good practice to use gap property. Margins can be defined using margin-block-start, margin-block-end, margin-inline-start, and margin-inline-end. The same applies to paddings. They can be defined with padding-block-start, padding-block-end, padding-inline-start and padding-inline-end. 

For left and right properties used for positioning elements, there are no similar alternatives. Styles can be adjusted by using CSS selectors. This approach is based on the fact that <html> tag has a dir attribute set.

[dir='ltr'] & {
    right: 1rem;
}
[dir='rtl'] & {
     left: 1rem;
}

Adapting page for RTL layouts

Right to left layout implementation in React

PostCSS plugins for the rescue

When we have a large existing application, manually adapting styles would be time-consuming. Fortunately, there are production-ready solutions that can help with that problem almost out of the box. One of them is postcss plugin postcss-rtlcss. It adds RTL versions of styles to our stylesheets whenever it encounters usage of CSS properties that are left or right-oriented.

In the example shown before, such styles would be generated automatically. It’s also capable of preparing styles for shorthand properties like margin or padding, by swapping left and right-related values. At this point, our application should be almost ready. What’s missing to achieve the final result?

Flip directional images, e.g., arrows

Some images or icons might have directionality. One of the examples might be left or right arrow icons. When the page flows in the opposite direction, the arrow should most likely be reversed. We need to identify every case of that type in our application and reverse such images. This can be achieved with transform CSS property and scaleX CSS function. It allows us to define a transformation that resizes elements along the x-axis. When -1 is provided as its parameter, the image would be simply flipped in the opposite direction.

[dir='rtl'] & {
    transform: scaleX(-1);
}

Fix translating elements over x-axis

Translating elements over the x-axis is another case that won’t be automatically handled by postcss-rtlcss. In RTL mode, elements should probably be translated in the opposite direction. An example of this case can be centering fixed-positioned elements:

position: fixed;
left: 50%;
transform: translateX(-50%);

postcss-rtlcss will add the following styles to our stylesheet

[dir='rtl'] & {
    right: 50%;
}

However, transform property will stay the same. This will result in an element that’s shifted to the left and not centered horizontally. To fix that, we need to apply a simple change to our CSS file that will transform the item to the right in RTL mode.

[dir='rtl'] & {
    transform: translateX(50%);
}

How to disable PostCSS transform for some cases

postcss-rtlcss can be disabled for some pieces of code. This is useful when we don’t want it to generate RTL styles for some part of the application or we want to handle RTL styles ourselves. In this example, the plugin won’t generate RTL-related code into the resulting CSS file, and the items with .item class will always be positioned to the left regardless of directionality. Note that comments are marked as important so that they won’t be removed by the loader, and postcss plugin will be able to access them.

/*! rtl:begin:ignore */
.item {
    left: 0;
}
/*! rtl:end:ignore */

Handling texts with mixed directionality

Sometimes not all translations for a given language are ready and some parts of the system are displayed in the default language, possibly of different directionality. Usually, the bidi algorithm should handle such situations, but that’s not always the case. Not all Unicode characters are associated with some directionality. Some of them are considered to be weak or neutral characters. 

Examples include punctuation, spaces or numbers. The way they are displayed is determined by the surrounding text. If they are surrounded by 2 characters of the same directionality, the inferred directionality is the same. If they are surrounded by 2 characters of different directionality, the inferred directionality is based on base direction. Consider the following example.

RTL in React - mixed directionality1

The text is in English, but the current language of the application is set to Arabic, so the base direction is set to RTL. Because of that, and the fact that the last character (dot) has weak directionality, the dot is placed just like the whole paragraph was Arabic.

Fortunately, there is a solution to this problem. Whenever the texts are dynamic, and we are not sure about directionality, we can use <bdi> HTML tag. It lets us treat text in isolation from its surroundings. In this case, by using <bdi> instead of <p>, the whole text is treated as LTR and the punctuation is fixed.

RTL in React - mixed directionality

Conclusion

To sum up, adding RTL languages to frontend applications for the first time can be painless, even if an app was written only with LTR languages in mind. Thanks to postcss plugins, most of the tedious work can be omitted, and the app is RTL-ready with minimal effort. All we need to do is make minor CSS changes whenever the postcss plugin wasn’t able to perform appropriate code generation.

The last step would be an app review by someone who natively speaks a given language. After all, people who browse websites displayed only in LTR languages might not notice if something’s wrong. If you need help with developing a web app you can reach out to the LeanCode team.

Free product design tips
Join our newsletter and get insights that will change your product design perspective
By submitting your email you agree to receive the content requested and to LeanCode's Privacy Policy.
or follow us
Meet our expert

We can help you build your app!

Send us a message, and we will find a solution and service that suits you best.
Rate this article
4.56 / 5 Based on 18 reviews

Read more

Find the most important things to keep in mind when introducing RTL design in your Flutter mobile app and how you can use Flutter to support various text directions with little effort. Follow the guide on RTL prepared by Flutter Developer.
RTL in Flutter by LeanCode
The first stable version of Patrol – a powerful, open-source UI testing framework for Flutter apps is out there, waiting for you to use it. Today, we put the “1.0” sticker on it. Find out how Patrol was born, our vision for it, what we improved, and what it is capable of now.
Testing Flutter Apps Patrol 1.0
SaaS app development requires meticulous planning and execution to deliver a robust solution that will satisfy end-users and allow you to earn money on the SaaS solution you offer. This article will help you understand what is necessary to build a functional SaaS application.
A web page of SaaS product