AniUI

Direction Provider

RTL/LTR direction context for right-to-left language support.

Installation#

npx @aniui/cli add direction-provider

Hello World

LTR

This is a left-to-right layout example.

Search...
Submit
@
you@example.com
Web preview — components render natively on iOS & Android
import { DirectionProvider } from "@/components/ui/direction-provider";

export default function App() {
  return (
    <DirectionProvider defaultDirection="rtl">
      {/* Your app content */}
    </DirectionProvider>
  );
}

Usage#

app/_layout.tsx
import { DirectionProvider } from "@/components/ui/direction-provider";

export default function App() {
  return (
    <DirectionProvider defaultDirection="rtl">
      {/* Your app content */}
    </DirectionProvider>
  );
}

useDirection Hook#

Access the current direction, RTL state, and direction controls from any child component.

Using useDirection
import { useDirection } from "@/components/ui/direction-provider";

export function LanguageToggle() {
  const { direction, isRTL, setDirection } = useDirection();

  return (
    <Button onPress={() => setDirection(isRTL ? "ltr" : "rtl")}>
      Current: {direction.toUpperCase()}
    </Button>
  );
}
PropTypeDefault
direction
"ltr" | "rtl"
-
isRTL
boolean
-
setDirection
(dir: Direction) => void
-

Props#

PropTypeDefault
children
React.ReactNode
required
defaultDirection
"ltr" | "rtl"
system default
className
string
-

Also accepts all View props from React Native.

Important#

Calling setDirection updates the React context immediately, but the full layout flip via I18nManager requires an app restart. This is a React Native limitation.

Accessibility#

  • Respects I18nManager for system-level RTL detection.
  • Supports right-to-left languages such as Arabic, Hebrew, and Persian.
  • Uses logical properties so layout adapts to direction context.

Source#

components/ui/direction-provider.tsx
import React, { createContext, useContext, useState, useCallback } from "react";
import { I18nManager, View } from "react-native";
import { cn } from "@/lib/utils";

type Direction = "ltr" | "rtl";

interface DirectionContextValue {
  direction: Direction;
  isRTL: boolean;
  setDirection: (dir: Direction) => void;
}

const DirectionContext = createContext<DirectionContextValue>({
  direction: I18nManager.isRTL ? "rtl" : "ltr",
  isRTL: I18nManager.isRTL,
  setDirection: () => {},
});

export function useDirection() {
  return useContext(DirectionContext);
}

export interface DirectionProviderProps extends React.ComponentPropsWithoutRef<typeof View> {
  children: React.ReactNode;
  defaultDirection?: Direction;
  className?: string;
}

export function DirectionProvider({
  children,
  defaultDirection,
  className,
  ...props
}: DirectionProviderProps) {
  const initial = defaultDirection ?? (I18nManager.isRTL ? "rtl" : "ltr");
  const [direction, setDir] = useState<Direction>(initial);

  const setDirection = useCallback((dir: Direction) => {
    setDir(dir);
    I18nManager.allowRTL(true);
    I18nManager.forceRTL(dir === "rtl");
  }, []);

  return (
    <DirectionContext.Provider
      value={{ direction, isRTL: direction === "rtl", setDirection }}
    >
      <View className={cn("flex-1", className)} {...props}>
        {children}
      </View>
    </DirectionContext.Provider>
  );
}