diff --git a/packages/core/index.ts b/packages/core/index.ts
index 5707de41c3b..9b72b72c4c2 100644
--- a/packages/core/index.ts
+++ b/packages/core/index.ts
@@ -29,6 +29,7 @@ export * from './useDeviceMotion'
export * from './useDeviceOrientation'
export * from './useDevicePixelRatio'
export * from './useDevicesList'
+export * from './useDir'
export * from './useDisplayMedia'
export * from './useDocumentVisibility'
export * from './useDraggable'
diff --git a/packages/core/useDir/demo.vue b/packages/core/useDir/demo.vue
new file mode 100644
index 00000000000..6178dc9b216
--- /dev/null
+++ b/packages/core/useDir/demo.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+ {{ text }}
+
+
+
+
Click to change the direaction
+
+
+
+
diff --git a/packages/core/useDir/index.md b/packages/core/useDir/index.md
new file mode 100644
index 00000000000..ad674a4246f
--- /dev/null
+++ b/packages/core/useDir/index.md
@@ -0,0 +1,35 @@
+---
+category: Browser
+---
+
+# useDir
+
+Reactive [dir](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) of the element's text.
+
+## Basic Usage
+
+```ts
+import { useDir } from '@vueuse/core'
+
+const dir = useDir() // Ref<'ltr' | 'rtl' | 'auto'>
+
+```
+By default, which enables `rlt` direction when dir `rtl` is applied to the `html` tag, for example:
+
+```html
+
+ ...
+
+
+ ...
+```
+
+## Config
+
+```ts
+import { useDir } from '@vueuse/core'
+
+const mode = useDir({
+ selector: 'body'
+}) // Ref<'ltr' | 'rtl' | 'auto'>
+```
diff --git a/packages/core/useDir/index.ts b/packages/core/useDir/index.ts
new file mode 100644
index 00000000000..49715060d42
--- /dev/null
+++ b/packages/core/useDir/index.ts
@@ -0,0 +1,61 @@
+import { ref, watch } from 'vue-demi'
+
+import type { MaybeElement } from '../unrefElement'
+import { useMutationObserver } from '../useMutationObserver'
+
+export interface UseDirOptions {
+ /**
+ * CSS Selector for the target element applying to
+ *
+ * @default 'html'
+ */
+ selector?: string
+ /**
+ * Observe `document.querySelector(selector)` changes using MutationObserve
+ *
+ * @default false
+ */
+ observe?: boolean
+}
+
+/**
+ * Reactive dir of the element's text.
+ *
+ * @see https://vueuse.org/useDir
+ * @param options
+ */
+export const useDir = (
+ options: UseDirOptions = {},
+) => {
+ const {
+ selector = 'html',
+ observe = false,
+ } = options
+
+ const defaultDir = 'ltr'
+ const dir = ref(document.querySelector(selector)?.getAttribute('dir') ?? defaultDir)
+
+ const isSupportedDir = (dir: string) => {
+ return ['ltr', 'rtl', 'auto'].includes(dir)
+ }
+
+ watch(dir, () => {
+ if (isSupportedDir(dir.value))
+ document.querySelector(selector)?.setAttribute('dir', dir.value)
+ else
+ document.querySelector(selector)?.removeAttribute('dir')
+ }, { immediate: true })
+
+ if (observe && document) {
+ useMutationObserver(
+ document.querySelector(selector) as MaybeElement,
+ () => {
+ const crtDir = document.querySelector(selector)?.getAttribute('dir') ?? defaultDir
+ dir.value = isSupportedDir(crtDir) ? crtDir : defaultDir
+ },
+ { attributes: true },
+ )
+ }
+
+ return dir
+}