Skip to main content

RTL support

EnrichedMarkdownText resolves writing direction per paragraph on iOS, Android and web: each paragraph picks its own base direction from its first strong directional character. Arabic, Hebrew, and Persian content right-aligns automatically, even inside an LTR app and even when mixed with English paragraphs in the same document.

The React Native package's editor (EnrichedMarkdownTextInput) follows the same per-paragraph rules; see its reference for input-specific caveats (placeholder, code blocks).

No setup required​

Every package resolves direction without configuration.

In the React Native package, every target resolves direction without configuration, by a different mechanism.

  • Android uses the platform TEXT_DIRECTION_FIRST_STRONG heuristic on every layout. Paragraphs with no strong character fall back to the view's resolved layout direction (which inherits an ancestor <View style={{ direction: 'rtl' }}> and I18nManager.isRTL).
  • iOS TextKit does not do per-paragraph first-strong on its own - it follows the app's global UI layout direction - so the library implements first-strong itself as a post-render pass, matching Android. The mode is controlled by the writingDirection prop and defaults to 'first-strong'.
  • Web gives each top-level block dir="auto", so the browser resolves it from its first strong character, like native, and CSS logical properties put blockquote borders, list markers and unaligned table cells on the matching side. The editor does the same per line. Setting the dir prop to 'ltr' or 'rtl' forces one direction for the whole document instead.
caution

On web a list takes one direction, from its first item, rather than one per item, and the table wrapper's table.align uses physical marginLeft/marginRight and does not flip.

note

Earlier versions documented I18nManager.forceRTL(true) as a requirement on iOS. That is no longer needed for content direction - first-strong resolves each paragraph from its content. I18nManager.forceRTL still affects the surrounding app layout and remains useful for a fully RTL app, but it is not a precondition for Markdown to render right-aligned.

Controlling direction​

In the React Native package, the iOS writingDirection prop selects how each paragraph's base direction is resolved:

ValueBehavior
'first-strong' (default)Per-paragraph autodetection; neutral-only paragraphs fall back to the view's resolved layout direction. Matches Android.
'auto'React Native parity: TextKit follows the app's userInterfaceLayoutDirection; mixed-direction documents do not auto-resolve.
'ltr'Forces LTR on every paragraph.
'rtl'Forces RTL on every paragraph.

Android ignores the prop (it always uses the platform first-strong heuristic); on web, use the dir prop instead. Code blocks always render LTR regardless.

Element behavior​

Each element follows the paragraph it belongs to, not a global flag, so a single document can mix sides cleanly:

ElementBehavior
Paragraphs and headingsBase direction set per paragraph from first-strong (or forced by the prop).
Unordered / ordered listsBullet or number drawn on the side that matches the item's paragraph direction.
Task listsCheckbox drawn on the matching side; the tap hit-test follows the same side.
BlockquotesAccent bar drawn on the side that matches the quoted paragraph.
Tables (flavor="github")On iOS, each cell resolves its own direction from its content. On Android, cells are pinned to the device locale (TEXT_DIRECTION_LOCALE), and column order and alignment follow the app's locale layout direction rather than the view's - so an Arabic cell in an English-locale app lays out LTR. On web, each cell resolves its own direction and column order follows the container.
Code blocksAlways LTR on iOS and web, and on Android under flavor="github" (the container pins it). Under flavor="commonmark" Android has no pin, so a code block whose first strong character is RTL takes an RTL base direction.
Inline codeInherits its paragraph's direction on native; characters flow correctly via the platform Bidi algorithm. On web it is pinned direction: ltr with unicode-bidi: embed, so it does not inherit.

Neutral content​

In the React Native package, for paragraphs with no strong directional character (digits, punctuation), the direction falls back to the view's resolved layout direction. So "123 456." inside a <View style={{ direction: 'rtl' }}> right-aligns, and left-aligns otherwise - letting you place an LTR document in an RTL screen (or vice versa) without surprises. On web, such a block gets no dir and inherits the container's direction. In the standalone iOS SDK, the fallback is the SwiftUI layoutDirection environment value.

Copy-as-HTML caveat​

When you copy content to the clipboard, the HTML representation carries at most a single dir attribute. In the React Native package, the two platforms derive it differently:

  • iOS and macOS read it from the document's first paragraph (it can also be "auto"), and set it on <html>, or on <table> for a table copy.
  • Android reads the app's locale layout direction (its resource configuration, not the view) instead, and only emits anything in the RTL case: <html dir="rtl"> plus a wrapping <div dir="rtl">. An LTR copy gets a bare <html> with no dir, and a table export carries no dir at all.

Receivers (Gmail, Notes, Word) then apply their own Bidi algorithm to the pasted HTML, so a mixed-direction document will not reproduce the exact per-paragraph layout you see in-app - HTML's dir is scoped to elements, not to paragraphs within them. The plain-text and Markdown clipboard representations are unaffected and round-trip cleanly.

The standalone SDKs:

  • Android takes the dir from the device locale, and a table export carries no dir at all.
  • iOS copies a table from its long-press menu as plain text only, so no HTML dir applies there.

Reference​