Skip to content

useTextDirection()

Category
Export Size
955 B
Gzipped
428 B
SSR
SSR support

Last updated: 24/04/2026

Overview

useTextDirection reads document.documentElement.getAttribute('dir'), normalizes anything other than rtl to ltr, and keeps a MutationObserver on <html> filtered to the dir attribute so React state updates when your app, a framework, or an i18n layer flips global direction for Arabic/Hebrew locales or accessibility testing. SSR returns ltr because document is absent; the observer is only installed in the browser, so there is no polling cost after the initial read.

What it accepts

  • None.

What it returns

  • TextDirection - 'ltr' | 'rtl'.

Usage

Show the live direction and buttons that set document.documentElement.dir (demo-only; in real apps i18n usually owns this).

tsx
import useTextDirection from '@dedalik/use-react/useTextDirection'

function Example() {
  const direction = useTextDirection()

  return (
    <div dir={direction} style={{ maxWidth: 420 }}>
      <h3>Document direction</h3>
      <p>
        Observed <code>document.documentElement</code>: <strong>{direction}</strong>
      </p>
      <p style={{ textAlign: direction === 'rtl' ? 'end' : 'start' }}>
        Sample paragraph aligns to the logical start edge.
      </p>
      <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
        <button type='button' onClick={() => (document.documentElement.dir = 'ltr')}>
          Set LTR
        </button>
        <button type='button' onClick={() => (document.documentElement.dir = 'rtl')}>
          Set RTL
        </button>
      </div>
    </div>
  )
}

export default function Demo() {
  return <Example />
}

API Reference

useTextDirection

Signature: useTextDirection(): TextDirection

Parameters

None.

Returns

TextDirection - 'ltr' | 'rtl' derived from the root dir attribute.

Copy-paste hook

TypeScript

tsx
import { useEffect, useState } from 'react'

export type TextDirection = 'ltr' | 'rtl'

function readDirection(): TextDirection {
  if (typeof document === 'undefined') return 'ltr'
  const dir = (document.documentElement.getAttribute('dir') || 'ltr').toLowerCase()
  return dir === 'rtl' ? 'rtl' : 'ltr'
}

/**
 * Tracks document text direction (`ltr` or `rtl`).
 */
export default function useTextDirection(): TextDirection {
  const [direction, setDirection] = useState<TextDirection>(() => readDirection())

  useEffect(() => {
    if (typeof document === 'undefined') return

    const observer = new MutationObserver(() => {
      setDirection(readDirection())
    })

    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['dir'],
    })

    return () => observer.disconnect()
  }, [])

  return direction
}

JavaScript

js
import { useEffect, useState } from 'react'

function readDirection() {
  if (typeof document === 'undefined') return 'ltr'
  const dir = (document.documentElement.getAttribute('dir') || 'ltr').toLowerCase()
  return dir === 'rtl' ? 'rtl' : 'ltr'
}

export default function useTextDirection() {
  const [direction, setDirection] = useState(() => readDirection())

  useEffect(() => {
    if (typeof document === 'undefined') return

    const observer = new MutationObserver(() => {
      setDirection(readDirection())
    })

    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['dir'],
    })

    return () => observer.disconnect()
  }, [])

  return direction
}

Released under the MIT License.