- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Form
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Pin Input
- Popover
- Progress
- Radio Group
- Range Calendar
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Stepper
- Switch
- Table
- Tabs
- Tags Input
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
- Typography
shadcn-vue components have first-class support for right-to-left (RTL) layouts. Text alignment, positioning, and directional styles automatically adapt for languages like Arabic, Hebrew, and Persian.
When you install components, the CLI automatically transforms physical positioning classes to logical equivalents, so your components work seamlessly in both LTR and RTL contexts.
Get Started
Enable RTL
Create a new project using the --rtl flag:
pnpm dlx shadcn-vue@latest create --rtl
This will create a components.json file with the rtl: true flag.
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "nova",
"rtl": true
}For an existing project, set rtl: true in your components.json and see Migrating existing components below.
Set the direction
Add the dir="rtl" and lang="ar" attributes to the html tag. Update lang="ar" to your target language.
For Vite, set them in your index.html:
<!doctype html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>For Nuxt, set them via useHead in your app.vue:
<script setup lang="ts">
useHead({
htmlAttrs: {
lang: 'ar',
dir: 'rtl',
},
})
</script>Add ConfigProvider
Wrap your app with the ConfigProvider component from reka-ui with the dir="rtl" prop, so all components read the correct direction:
<script setup lang="ts">
import { ConfigProvider } from 'reka-ui'
</script>
<template>
<ConfigProvider dir="rtl">
<RouterView />
</ConfigProvider>
</template>Add Font
For the best RTL experience, we recommend using fonts that have proper support for your target language. Noto is a great font family for this and it pairs well with Inter and Geist.
Install the font using Fontsource:
pnpm add @fontsource-variable/noto-sans-arabic
Import the font in your css file:
@import "tailwindcss";
@import "tw-animate-css";
@import "@fontsource-variable/noto-sans-arabic";
@theme inline {
--font-sans: "Noto Sans Arabic Variable", sans-serif;
}For other languages, eg. Hebrew, you can use @fontsource-variable/noto-sans-hebrew.
Add Components
You are now ready to add components to your project. The CLI will take care of handling RTL support for you.
pnpm dlx shadcn-vue@latest add card
How it works
When you add components with rtl: true set in your components.json, the CLI automatically transforms classes to be RTL compatible:
- Physical positioning classes like
left-*andright-*are converted to logical equivalents likestart-*andend-*. - Text alignment and spacing classes are adjusted accordingly.
- Supported icons are automatically flipped using
rtl:rotate-180.
Font Recommendations
For the best RTL experience, we recommend using fonts that have proper support for your target language. Noto is a great font family for this and it pairs well with Inter and Geist.
See the Get Started section for details on installing and configuring RTL fonts.
Animations
The CLI also handles animation classes, automatically transforming physical directional animations to their logical equivalents. For example, slide-in-from-right becomes slide-in-from-end.
This ensures animations like dropdowns, popovers, and tooltips animate in the correct direction based on the document's text direction.
A note on tw-animate-css:
There is a known issue with the tw-animate-css library where the logical slide utilities are not working as expected. For now, make sure you pass in the dir attribute to portal elements.
<Popover>
<PopoverTrigger>Open</PopoverTrigger>
<PopoverContent dir="rtl">
<div>Content</div>
</PopoverContent>
</Popover><Tooltip>
<TooltipTrigger>Open</TooltipTrigger>
<TooltipContent dir="rtl">
<div>Content</div>
</TooltipContent>
</Tooltip>Migrating existing components
If you have existing components installed before enabling RTL, you can migrate them using the CLI as follows:
Run the migrate command
pnpm dlx shadcn-vue@latest migrate rtl [path]
[path] accepts a path or glob pattern to migrate. If you don't provide a path, it will migrate all the files in the ui directory.
# Migrate a specific file
npx shadcn-vue@latest migrate rtl src/components/ui/button/Button.vue
# Migrate files matching a glob pattern
npx shadcn-vue@latest migrate rtl "src/components/ui/**"This will also update your components.json to set rtl: true. See the CLI documentation for more details.
Manual Migration (Optional)
The following components are not automatically migrated by the CLI and may need manual adjustments:
Migrate Icons
Some icons like ArrowRight or ChevronLeft might need the rtl:rotate-180 class to be flipped correctly. Add the rtl:rotate-180 class to the icon component to flip it correctly.
<ArrowRight class="rtl:rotate-180" />Add ConfigProvider
Wrap your app with the ConfigProvider component from reka-ui. See the Get Started section for details.