Skip to main content

direction

Set RTL or LTR once and every component follows.

Philosophy

Direction is cross-cutting state. Set it once and every component works out the rest: layout, spacing, icons, keyboard arrows, and where overlays open. You do not pass dir to components or add rtl: classes.

Installation


npx @gentleduck/cli add direction

npx @gentleduck/cli add direction

Usage

Set the direction on the document:

<html dir="rtl" lang="ar">

Or wrap your app, or any part of it, in DirectionProvider:

import { DirectionProvider } from '@/components/ui/direction'

<DirectionProvider dir="rtl">{children}</DirectionProvider>

Components resolve direction from their own dir prop, then the nearest DirectionProvider, then <html dir>. Changes to <html dir> are picked up at runtime. On the server the document is not available, so in server-rendered apps use DirectionProvider (it can sit right inside <html dir="rtl">) to get the right direction on first paint.

Custom components

Use useDirection when your own component needs the direction in JavaScript, such as picking an icon or flipping a key. For layout, prefer logical classes (ms-*, pe-*, start-*, text-start), which follow dir without any code.

import { ArrowLeft, ArrowRight } from 'lucide-react'
import { useDirection } from '@/components/ui/direction'

function NextIcon() {
  const dir = useDirection()
  return dir === 'rtl' ? <ArrowLeft /> : <ArrowRight />
}

API Reference

Exports

ExportTypeDescription
DIRECTION_DICTIONARY{ ltr: 'ltr'; rtl: 'rtl' }Canonical direction values
DirectionProviderReact.FC<{ dir: 'ltr' | 'rtl'; children?: ReactNode }>Provides direction to descendants
ProviderReact.FC<{ dir: 'ltr' | 'rtl'; children?: ReactNode }>Alias for DirectionProvider
useDirection(localDir?: 'ltr' | 'rtl') => 'ltr' | 'rtl'Resolves localDir -> nearest DirectionProvider -> <html dir>
DirectionContextReact.Context<'ltr' | 'rtl' | undefined>Underlying context object
toDirection(dir: unknown) => 'ltr' | 'rtl' | undefinedNarrows an arbitrary dir value; anything else becomes undefined