Sveltery UI components support right-to-left (RTL) layouts. Text alignment, positioning and directional styles adapt for languages such as Arabic, Hebrew and Persian. Sveltery Base reads the direction for keyboard navigation and popup placement.
When you add components, the CLI turns physical positioning classes into logical ones, so your components work in both LTR and RTL layouts.
Get started
Turn on RTL in components.json
Run init with the --rtl flag. You can skip this step if components.json already has "rtl": true.
This writes rtl: true to components.json. Presets take it too, as init --preset nova --rtl or rtl=true on an /init URL.
{
"$schema": "https://ui.sveltery.dev/schema.json",
"style": "nova",
"rtl": true
} Set the direction on the document
Add dir="rtl" and your language to the html tag in src/app.html. Change lang="ar" to your target language.
<!doctype html>
<html lang="ar" dir="rtl">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="text-scale" content="scale" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html> Add DirectionProvider
Add the direction component to your project.
Wrap your app in DirectionProvider with direction="rtl" in your root layout. Base components read it to reverse arrow keys and to place popups.
<script lang="ts">
import './layout.css';
import { DirectionProvider } from '#lib/components/ui/direction';
let { children } = $props();
</script>
<DirectionProvider direction="rtl">
{@render children()}
</DirectionProvider> DirectionProvider renders no element, so the dir attribute stays yours to set. See Direction to switch the direction at runtime.
Add a font
Use a font that supports your target language. Noto is a good choice and pairs well with Inter and Geist.
Install the font from Fontsource:
Import it in your CSS file and set it as the sans font:
@import "tailwindcss";
@import "tw-animate-css";
@import "@sveltery/ui/tailwind.css";
@import "@fontsource-variable/noto-sans-arabic";
@theme inline {
--font-sans: "Noto Sans Arabic Variable", sans-serif;
} For Hebrew, use @fontsource-variable/noto-sans-hebrew.
Add components
You are ready to add components. The CLI handles RTL support for you.
How it works
When you add components with rtl: true in components.json, the CLI changes their classes and props:
- Physical positioning classes such as
left-*andright-*become logical ones such asstart-*andend-*. - Directional props, such as a submenu's
side="right", take logical values such asside="inline-end". - Text alignment and spacing classes change the same way, for example
text-lefttotext-startandml-*toms-*. - Arrow and chevron icons that the registry marks get
rtl:rotate-180, so they flip.
The CLI uses the same mappings as the shadcn CLI. The examples on the component pages that show an RTL layout render through the same transform.
Animations
Dropdowns, popovers and tooltips animate in the direction of the text without a change from the CLI: their slide animations come from the style's CSS, which keys them on the logical data-[side=inline-start] and data-[side=inline-end] sides. In your own classes, the CLI changes a slide inside a logical side, such as data-[side=inline-start]:slide-in-from-right, to slide-in-from-end.
Popups outside the document direction
Popovers, tooltips and menus render in a portal at the end of <body>. With dir on <html>, they inherit it. If you set dir on an inner element instead, pass dir to the popup content as well:
<Popover>
<PopoverTrigger>Open</PopoverTrigger>
<PopoverContent dir="rtl">
<div>Content</div>
</PopoverContent>
</Popover> Migrating existing components
If you installed components before you turned on RTL, migrate them with the CLI.
[path] takes a file, a directory or a glob pattern with *, which needs Node 22 or later. Without it, the command migrates every file in the ui directory. It also sets rtl: true in components.json, so later add runs transform too. A file it cannot parse is skipped with a warning.
The flip markers on arrow and chevron icons were removed when you added the components, so the migration cannot add rtl:rotate-180 back to them. Run update afterwards to rewrite your components from the registry with the flips:
update asks before it overwrites each changed file, so keep your own edits where you need them.
Manual Migration (Optional)
The CLI does not migrate the following components. Check them by hand:
- Calendar: pass
dir="rtl"and a right-to-left locale. - Breadcrumb, Carousel, Dropdown Menu and Context Menu: add
rtl:rotate-180to the separator, previous and next, and sub-trigger chevron icons, or runupdate. - Pagination: add
rtl:rotate-180to the icons inPaginationPrevious.svelteandPaginationNext.svelteif they do not flip. - Sidebar: check the side the sidebar opens from, and add
rtl:rotate-180to the icon inSidebarTrigger.svelteif it does not flip.
Migrate icons
Icons such as ArrowRightIcon or ChevronLeftIcon in your own code need the rtl:rotate-180 class to flip:
<ArrowRightIcon class="rtl:rotate-180" /> Add the direction component
Add the direction component, then wrap your app in DirectionProvider as Get started shows.