useRtl
Right-to-left text direction management with reactive state and subtree overrides.
Installation
Install the RTL plugin in your app’s entry point:
import { createApp } from 'vue'
import { createRtlPlugin } from '@vuetify/v0'
import App from './App.vue'
const app = createApp(App)
app.use(createRtlPlugin())
app.mount('#app')With Options
app.use(
createRtlPlugin({
default: true, // Start in RTL mode
})
)Usage
import { useRtl } from '@vuetify/v0'
const rtl = useRtl()
// Read direction
rtl.isRtl.value // false (LTR by default)
// Toggle direction
rtl.toggle() // Now true (RTL)
// Direct assignment
rtl.isRtl.value = false // Back to LTRAdapters
Adapters let you swap the underlying dir attribute management without changing your application code.
| Adapter | Import | Description |
|---|---|---|
V0RtlAdapter | @vuetify/v0 | Sets dir on target element (default) |
The default adapter sets the dir attribute on the target element (defaults to document.documentElement), enabling native browser RTL support including CSS logical properties and the :dir() pseudo-class.
// Custom target element
app.use(createRtlPlugin({ target: '#app' }))
// Disable dir attribute management
app.use(createRtlPlugin({ target: null }))Custom Adapters
Implement RtlAdapter to control how RTL direction is applied to the DOM:
import type { RtlAdapter } from '@vuetify/v0'
class CustomRtlAdapter extends RtlAdapter {
setup (app, context, target) {
// context.isRtl — reactive ref, write to it to change direction
// context.toggle — flips isRtl
watchEffect(() => {
const el = typeof target === 'string' ? document.querySelector(target) : target
if (el) el.setAttribute('dir', context.isRtl.value ? 'rtl' : 'ltr')
})
}
}
app.use(createRtlPlugin({ adapter: new CustomRtlAdapter() }))abstract class RtlAdapter {
abstract setup<T extends RtlAdapterSetupContext> (app: App, context: T, target?: string | HTMLElement | null): void
}Architecture
useRtl is a standalone plugin built with createPluginContext; its entire state is one reactive boolean (isRtl). Applying that direction to the DOM is delegated to an RtlAdapter — the default V0RtlAdapter writes the dir attribute onto the target element (document.documentElement unless overridden). It is independent of useLocale; Vuetify wires the two together through its own adapter.
Reactivity
| Property | Type | Description |
|---|---|---|
isRtl | Ref<boolean> | Writable ref — true for RTL, false for LTR |
toggle | () => void | Flips the current direction |
Examples
This text flows from left to right. Notice how the entire layout adapts to the text direction.
useRtl() reads from the plugin context. Toggle updates isRtl globally.
Recipes
Standalone Usage
Use createRtl to create a raw RTL context without the plugin system — useful for testing or embedding in other composables:
import { createRtl } from '@vuetify/v0'
const rtl = createRtl({ default: true }) // starts in RTL
rtl.isRtl.value // true
rtl.toggle()
rtl.isRtl.value // falsecreateRtl accepts default?: boolean (initial direction) and returns { isRtl: ShallowRef<boolean>, toggle: () => void }. No app is required.
Styling
The dir attribute set by the adapter enables three approaches to direction-aware styling with utility classes:
Logical Properties (preferred)
CSS logical properties automatically flip based on dir. Use these by default:
<!-- Physical (breaks in RTL) -->
<div class="ml-4 pr-2 left-0 border-l-2">...</div>
<!-- Logical (works in both directions) -->
<div class="ms-4 pe-2 start-0 border-s-2">...</div>| Physical | Logical | CSS Property |
|---|---|---|
ml-* / mr-* | ms-* / me-* | margin-inline-start / end |
pl-* / pr-* | ps-* / pe-* | padding-inline-start / end |
left-* / right-* | start-* / end-* | inset-inline-start / end |
border-l-* / border-r-* | border-s-* / border-e-* | border-inline-start / end |
rounded-l-* / rounded-r-* | rounded-s-* / rounded-e-* | border-start-*-radius / end |
text-left / text-right | text-start / text-end | text-align |
The utility class names above use UnoCSS presetWind4 / Tailwind v4 syntax. Exact class names may vary depending on your CSS framework or preset — the underlying CSS logical properties are the same.
Direction Variants
For cases logical properties can’t handle (like translate-x), use the bare class as the LTR default and the rtl: variant as the override:
<!-- Mobile drawer: slides from start edge -->
<nav class="-translate-x-full rtl:translate-x-full md:translate-x-0">
...
</nav> Avoid the ltr: variant — it only applies when an ancestor has an explicit dir="ltr" attribute, not as the default. Use the bare utility class for LTR behavior and rtl: for the RTL override.
Symmetric Shorthand
When both sides use the same value, use inset-x-* instead of left-* right-*:
<!-- Before -->
<div class="fixed left-0 right-0 top-0">...</div>
<!-- After -->
<div class="fixed inset-x-0 top-0">...</div>Subtree Overrides
Use createRtlContext to scope direction to a subtree — isolated from the app-level direction:
import { createRtlContext } from '@vuetify/v0'
export const [useLocalRtl, provideLocalRtl, localRtl] =
createRtlContext({ default: true })<script setup lang="ts">
import { provideLocalRtl } from './rtl-context'
// Provide to all descendants
provideLocalRtl()
</script>
<template>
<slot />
</template><script setup lang="ts">
import { useLocalRtl } from './rtl-context'
const rtl = useLocalRtl()
// rtl.isRtl.value → true (isolated from app-level direction)
// rtl.toggle() → scoped toggle, doesn't affect the rest of the app
</script> Direction is independent from locale. To connect them (e.g., Arabic → RTL), use a custom adapter that watches useLocale().selectedId and sets isRtl based on a language→direction mapping.
FAQ
No — direction is independent from locale. To link them (e.g. Arabic → RTL), use a custom adapter that watches useLocale’s selectedId and sets isRtl from a language-to-direction mapping.
Use createRtlContext to provide a scoped direction via provide/inject. A descendant reading it is isolated from the app-level flag, so toggling it doesn’t affect the rest of the app.
The ltr: variant only applies when an ancestor carries an explicit dir="ltr" attribute, not as the default. Use the bare utility class for LTR behavior and rtl: for the override — e.g. -translate-x-full rtl:translate-x-full.
No — prefer CSS logical properties (ms/me, ps/pe, start/end), which flip automatically with the dir attribute. Reserve direction variants for the cases logical properties can’t express, like translate-x.