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 LTRReactivity
| Property | Type | Description |
|---|---|---|
isRtl | Ref<boolean> | Writable ref — true for RTL, false for LTR |
toggle | () => void | Flips the current direction |
Adapters
Adapters let you swap the underlying dir attribute management without changing your application code.
| Adapter | Import | Description |
|---|---|---|
Vuetify0RtlAdapter | @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 implements 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() }))interface RtlAdapter {
setup: (app: App, context: { isRtl: Ref<boolean>; toggle: () => void }, target?: string | HTMLElement | null) => void
}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.
Examples
Direction Toggle
Live RTL/LTR switcher showing isRtl toggling the dir attribute and text-alignment classes in real time.
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.
Functions
createRtlFallback
() => RtlContextcreateRtlContext
<_E>(_options?: RtlPluginOptions | undefined) => ContextTrinity<_E>createRtlPlugin
(_options?: RtlPluginOptions | undefined) => PluginuseRtl
<_E>(namespace?: string) => _EOptions
target
string | HTMLElement | null | undefinedTarget element or selector for dir attribute. Defaults to documentElement.