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
- 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>
- 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:
- English blocks inside RTL content are rendered in an RTL container, misaligning text
- Code blocks inherit the parent direction, potentially placing line numbers on the wrong side
display: contents on the Block wrapper breaks space-y-4 margin spacing between blocks
Before Fix (Bug)
After Fix (Expected)
Per-block direction: Hebrew → RTL, English → LTR, Code → always LTR
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.
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
Streamdownand render mixed RTL/LTR content in static mode:Expected Behavior
Each block should have its own text direction detected independently:
dir="rtl"(right-aligned)dir="ltr"(left-aligned)dir="ltr"(code is universally LTR)This is how streaming mode already behaves.
# שלום עולם## English Section## קטע קודpython ...## סיכוםActual Behavior
All blocks receive the same direction (RTL if the first strong character is RTL, LTR otherwise). This causes:
display: contentson the Block wrapper breaksspace-y-4margin spacing between blocksBefore Fix (Bug)
After Fix (Expected)
Code Sample
Streamdown Version
2.5.0
React Version
18.x / 19.x
Node.js Version
22.x
Browser(s)
Operating System
All (Linux, macOS, Windows)
Additional Context
parseMarkdownIntoBlocks+detectTextDirection.index.tsxwhere static mode callsdetectTextDirection(processedChildren)once on the entire markdown string and applies a singledirattribute to the outer container.detectTextDirectiondoes 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.