Skip to content

BidiText

XS · 350B gzip budget

Isolates user-controlled text so a mixed-direction sentence does not reorder around it.

Category
Internationalisation
Budget
xs, static display primitive
External runtime
React / React DOM only
axe fixtureSSR rendersource scanbundle budget

Preview

Live fixture
Preview locale

Reply from محمد (3 replies)

Try the component here. Locale-aware formatting and direction follow the selected language; example content and labels are not automatically translated. Overlay demos use interactive launchers.

Why this exists

BidiText

العربية

A right-to-left name next to a number pulls the number to the wrong side of it. Here the naive version renders the reply count before the name instead of after. The sentence reads backwards, and only for users with RTL names.

Formatting shortcut

إيان - 42 replies

<span>{name} - {n} replies</span>

BidiText

إيان - 42 replies

bidi-text

Install

npx shadcn@latest add https://gear5-ui.vercel.app/r/bidi-text.json

Copies the source into your project.

New here? Set up Tailwind and import aliases first →

Source & props

Edit on GitHub ↗

Prop types and inline documentation are included below. This is repository source; the installer rewrites shared imports for your project.

"use client";

export interface BidiTextProps {
  /** Text of unknown or user-controlled direction. */
  children: React.ReactNode;
  /**
   * "auto" lets the browser infer direction from the first strong character —
   * the right default for user-generated content whose language you do not
   * know at render time.
   */
  dir?: "auto" | "ltr" | "rtl";
}

/**
 * Isolates text whose direction you do not control.
 *
 * When an Arabic or Hebrew name is interpolated into an English sentence, the
 * Unicode bidirectional algorithm reorders the *surrounding* punctuation too:
 * "محمد (3 replies)" can render with the parenthesis and count on the wrong
 * side, and the sentence reads as scrambled to everyone. The bug is invisible
 * in development in an English locale, and reported constantly by users whose
 * names trigger it.
 *
 * `<bdi>` — bidirectional isolate — scopes the reordering to the untrusted run
 * so neighbouring text is unaffected. It is one element, has no styling, and
 * is the entire fix.
 *
 * Wrap any interpolated value you did not author: names, titles, filenames,
 * search terms, message bodies.
 */
export function BidiText({ children, dir = "auto" }: BidiTextProps) {
  return <bdi dir={dir}>{children}</bdi>;
}

What CI checks

Quality contract
  • Bundled, minified and gzipped against its tier budget
  • Audited by axe in the state previewed above
  • Rendered through react-dom/server with no browser globals
  • Scanned for network calls, dangerous sinks, and unguarded animation