Skip to content

Bug Report: dir="auto" in static mode applies single direction to all content #509

@shloimy-wiesel

Description

@shloimy-wiesel

Bug Description

When using <Streamdown mode="static" dir="auto"> with mixed RTL/LTR content, the component detects the text direction once for the entire markdown string and applies it uniformly to all blocks. This causes incorrect rendering when content mixes RTL (e.g., Hebrew, Arabic) and LTR (e.g., English, code blocks) text.

In streaming mode, direction is correctly detected per-block — but static mode does not have this behavior.

Steps to Reproduce

  1. Import Streamdown and render mixed RTL/LTR content in static mode:
import { Streamdown } from 'streamdown';

const content = `# שלום עולם

זהו טקסט בעברית שמדגים את הבעיה עם כיוון הטקסט.

## English Section

This is an English paragraph that should be left-to-right.

## קטע קוד

הנה דוגמה לקוד:

\`\`\`python
def hello():
    print("Hello World")
    return True
\`\`\`

## סיכום

הטקסט הזה צריך להיות מימין לשמאל, אבל הקוד צריך להישאר משמאל לימין.`;

<Streamdown mode="static" dir="auto">{content}</Streamdown>
  1. Observe that the entire output gets the same direction (based on the first strong character in the whole string), instead of each block getting its own direction.

Expected Behavior

Each block should have its own text direction detected independently:

  • Hebrew headings and paragraphs → dir="rtl" (right-aligned)
  • English headings and paragraphs → dir="ltr" (left-aligned)
  • Code blocks → always dir="ltr" (code is universally LTR)

This is how streaming mode already behaves.

Block Expected Direction
# שלום עולם RTL
## English Section LTR
## קטע קוד RTL
python ... LTR (always)
## סיכום RTL

Actual Behavior

All blocks receive the same direction (RTL if the first strong character is RTL, LTR otherwise). This causes:

  1. English blocks inside RTL content are rendered in an RTL container, misaligning text
  2. Code blocks inherit the parent direction, potentially placing line numbers on the wrong side
  3. display: contents on the Block wrapper breaks space-y-4 margin spacing between blocks

Before Fix (Bug)

Image

After Fix (Expected)

Per-block direction: Hebrew → RTL, English → LTR, Code → always LTR

Image

Code Sample

import { Streamdown } from 'streamdown';

// Bug: entire content gets RTL because first strong character is Hebrew
<Streamdown mode="static" dir="auto">
  {"# שלום\n\n## English Title\n\nEnglish paragraph.\n\n```js\nconsole.log('hello');\n```"}
</Streamdown>

Streamdown Version

2.5.0

React Version

18.x / 19.x

Node.js Version

22.x

Browser(s)

  • Chrome
  • Firefox
  • Safari
  • Edge

Operating System

All (Linux, macOS, Windows)

Additional Context

  • Streaming mode already handles this correctly — it detects direction per-block using parseMarkdownIntoBlocks + detectTextDirection.
  • The root cause is in index.tsx where static mode calls detectTextDirection(processedChildren) once on the entire markdown string and applies a single dir attribute to the outer container.
  • Additionally, detectTextDirection does not strip fenced code blocks before detection, so code content (which is always LTR) can skew the direction result for blocks that contain embedded code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions