RTL
View SourceIntroduction
This guide adds RTL (Right-to-Left) support to an app that already has locale set up. It covers setting the document direction (dir="rtl") from the current locale and ensuring Corex components (menus, dialogs, select, etc.) mirror correctly for languages like Arabic.
Prerequisites: You have followed the Locale guide and have a Locale plug, /:locale routes, and @locale / @current_path in assigns.
The Problem
For RTL locales (e.g. ar), you need to:
- Set
dir="rtl"on the root<html>so layout and text flow correctly. - Have the Locale plug and LiveView hooks assign
:dirso the root layout and any component that needs it can use it. - Rely on Corex components to derive direction from the document when
diris not passed explicitly.
The Solution
- Configure which locales are RTL via
config :corex, rtl_locales: ["ar"]. - In the Locale plug’s
set_locale, assign:dirfrom the current locale and that config. - In the root layout, set
dir={assigns[:dir] || "ltr"}on<html>(with an optional fallback fromlocale+rtl_locales). - In the shared LiveView hook, assign
:dirfromparams["locale"]and the same config. - Corex components that support direction read it from the document when
diris not passed; you can override per component withdir={@dir}if needed.
Implementation
1. Configuration
In config/config.exs (or env-specific config), add which locales are RTL:
config :corex,
gettext_backend: MyAppWeb.Gettext,
rtl_locales: ["ar"]Add every RTL locale code you support (e.g. ["ar", "fa", "he"]).
2. Locale Plug: Assign dir
In your Locale plug’s set_locale function, compute dir from the current locale and assign it:
defp set_locale(conn, locale) do
Gettext.put_locale(@backend, locale)
current_path = path_without_locale(conn.request_path, locale)
dir = if locale in Application.get_env(:corex, :rtl_locales, []), do: "rtl", else: "ltr"
conn
|> assign(:locale, locale)
|> assign(:dir, dir)
|> assign(:current_path, current_path)
end3. Root Layout: dir on <html>
Set dir on the root <html> from assigns so the initial render and LiveView both have the correct direction:
<!DOCTYPE html>
<html lang={assigns[:locale] || "en"} dir={assigns[:dir] || "ltr"} data-theme="neo" data-mode={assigns[:mode]}>
...
</html>If the root layout can run before the plug has set assigns[:dir] (e.g. first paint), you can derive dir from locale and config:
<html
lang={assigns[:locale] || "en"}
dir={assigns[:dir] || (if assigns[:locale] in Application.get_env(:corex, :rtl_locales, []), do: "rtl", else: "ltr")}
data-theme="neo"
data-mode={assigns[:mode]}
>This avoids FOUC and lets Corex components that read direction from the document get the right value.
4. Shared LiveView Hook: Assign dir
In the shared hook used by LiveViews under /:locale, assign :dir from the locale param and the same config:
def on_mount(:default, params, _session, socket) do
locale = params["locale"] || "en"
dir = if locale in Application.get_env(:corex, :rtl_locales, []), do: "rtl", else: "ltr"
socket =
socket
|> assign(:locale, locale)
|> assign(:dir, dir)
|> assign_current_path(params)
|> attach_hook(:locale_change, :handle_event, &handle_locale_change/3)
|> attach_hook(:current_path, :handle_params, &assign_current_path_from_uri/3)
{:cont, socket}
endPass @dir into the app layout if you use it in the layout (e.g. for a wrapper with dir={@dir}). The root <html> is rendered by the server on full page loads; LiveView updates the body, so dir on <html> is correct as long as the plug or initial LiveView mount sets it (e.g. via the root layout assigns).
5. Corex Components and Direction
- Document: Setting
dir="rtl"on<html>is enough for the page and for Corex components that derive direction from the document whendiris not passed. - Explicit
dir: Many Corex components (menu, dialog, select, combobox, tabs, etc.) accept an optionaldirattribute. If you passdir={@dir}, that overrides the document-derived value. Usually you only need the documentdirand the shared assigns. - Per-component override: For a single component that should differ from the page (e.g. a LTR menu on an RTL page), pass
dir="ltr"(or the opposite of@dir) explicitly.
Summary
- Config: Add
rtl_locales: ["ar"](and any other RTL locale codes) underconfig :corex, .... - Locale plug: In
set_locale, setdirfrom locale +rtl_localesandassign(conn, :dir, dir). - Root layout: Set
dir={assigns[:dir] || "ltr"}(or derive fromassigns[:locale]+rtl_locales) on<html>. - SharedEvents: On mount, assign
:dirfromparams["locale"]andrtl_locales. - Corex components: Use document
dirby default; passdir={@dir}only when you need to override or pass it explicitly.
This gives correct RTL layout and Corex component behavior when using RTL locales. For locale setup (URL, cookie, switcher), see the Locale guide.