Files
suaveplan/packages/localization/i18n/react-i18n/src/hooks/use-direction/use-direction.md
2026-05-27 23:47:53 +01:00

3.5 KiB

useDirection

(no submodules)

Purpose

useDirection() returns the writing direction ('ltr' / 'rtl') for the active locale. Composes useLocale (so it inherits the underlying useSyncExternalStore subscription) and runs the locale through @suaveplan/i18n's getTextDirection helper. The single-source-of-truth for which locales are RTL lives in the i18n core's RTL_LOCALES set — the React surface does not duplicate the list.

The hook is the canonical primitive for direction-sensitive UI: setting <html dir="...">, mirroring icon strips, swapping margin-left for margin-right in a CSS module, and so on. A locale change like setLocale('ar-AE') flips the result to 'rtl' on the same React commit cycle as the bus emission, so the UI re-mirrors atomically with the new translations.

Features

  • Returns { direction, locale } — both kept in sync with the active locale.
  • 'ltr' / 'rtl' resolution via the i18n core's getTextDirection.
  • direction defaults to 'ltr' on the production no-Provider fallback (the safer default for unknown locales).
  • Memoised on the locale string so a no-op re-render does not re-call getTextDirection.
  • Re-renders on languageChanged via the inherited useLocale subscription.

Usage: Basic Example

import { useDirection } from "@suaveplan/react-i18n";

export function ChatBubble(props: { readonly children: React.ReactNode }) {
    const { direction } = useDirection();
    return (
        <div
            dir={direction}
            style={{ textAlign: direction === "rtl" ? "right" : "left" }}
        >
            {props.children}
        </div>
    );
}

Usage: Advanced Example

For document-level mirroring set the <html dir> attribute from a top-level effect that mirrors the hook's output. The pattern below keeps the markup in sync without needing every component to be direction-aware.

import { useEffect } from "react";
import { useDirection } from "@suaveplan/react-i18n";

export function HtmlDirectionSync(): null {
    const { direction, locale } = useDirection();
    useEffect(() => {
        const root = document.documentElement;
        if (locale !== undefined) root.lang = locale;
        root.dir = direction;
    }, [direction, locale]);
    return null;
}

API Reference

useDirection(): UseDirectionReturn

  • Returns: { direction, locale }.
    • direction: 'ltr' | 'rtl' — derived from getTextDirection(locale). Defaults to 'ltr' when locale === undefined.
    • locale: string | undefined — current active locale, mirrored from useLocale().locale.
  • Throws: ReactI18nError(REACT_I18N.NO_PROVIDER) outside a <TranslationProvider> in dev mode (inherited from useLocale → useI18n).

Implementation Notes

  • The lookup is wrapped in useMemo keyed on the locale string. getTextDirection is cheap (a single Set.has after a string split), but the memo also keeps the returned object reference stable across re-renders that did not change the locale, which keeps React.memo-wrapped descendants from re-rendering.
  • The hook deliberately delegates the RTL list to @suaveplan/i18n instead of duplicating it. New RTL scripts added to the upstream RTL_LOCALES table propagate automatically — no React-side maintenance required.
  • The fallback to 'ltr' on the no-Provider production path matches the broader package contract: end-users see degraded but non-broken UI when configuration is missing.