feat: replace react-markdown with streamdown for jitter-free streaming - #17
Merged
Conversation
react-markdown re-parses the entire accumulated text on every token, producing different DOM trees for incomplete markdown and causing chat bubble height to oscillate during fast streaming. Streamdown (by Vercel) splits content into blocks and memoizes completed ones, so only the last in-progress block re-renders on each token. - Swap react-markdown + remark-gfm for streamdown - Add isStreaming prop through ChatBubble → MarkdownRenderer - Remove failed height-ratchet approach from ConversationView - Update tests for Streamdown's element rendering conventions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Code review found four issues:
1. Links rendered as dead <button> elements (linkSafety modal was
intercepting). Disable linkSafety so Streamdown renders native
<a> tags with href, target="_blank", and rel="noopener noreferrer".
Restore the href assertion in the link test.
2. CLAUDE.md architecture section said "DOMPurify sanitization" but
DOMPurify was never used. Update to reflect actual renderer
(Streamdown with rehype-sanitize).
3. Security rationale JSDoc was deleted in the streamdown migration.
Add documentation of Streamdown's XSS protection model.
4. Add inline comments explaining controls={false} and
linkSafety={{ enabled: false }} prop choices.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
mermaid (transitive dep via streamdown) pins lodash-es to 4.17.23 which has known Code Injection and Prototype Pollution vulnerabilities. Override to >=4.18.0 which is outside the vulnerable range (<=4.17.23). mermaid is tree-shaken and never loaded at runtime. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
This was referenced Apr 5, 2026
quiet-node
added a commit
that referenced
this pull request
Apr 10, 2026
#17) * feat: replace react-markdown with streamdown for jitter-free streaming react-markdown re-parses the entire accumulated text on every token, producing different DOM trees for incomplete markdown and causing chat bubble height to oscillate during fast streaming. Streamdown (by Vercel) splits content into blocks and memoizes completed ones, so only the last in-progress block re-renders on each token. - Swap react-markdown + remark-gfm for streamdown - Add isStreaming prop through ChatBubble → MarkdownRenderer - Remove failed height-ratchet approach from ConversationView - Update tests for Streamdown's element rendering conventions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: restore functional links and update security documentation Code review found four issues: 1. Links rendered as dead <button> elements (linkSafety modal was intercepting). Disable linkSafety so Streamdown renders native <a> tags with href, target="_blank", and rel="noopener noreferrer". Restore the href assertion in the link test. 2. CLAUDE.md architecture section said "DOMPurify sanitization" but DOMPurify was never used. Update to reflect actual renderer (Streamdown with rehype-sanitize). 3. Security rationale JSDoc was deleted in the streamdown migration. Add documentation of Streamdown's XSS protection model. 4. Add inline comments explaining controls={false} and linkSafety={{ enabled: false }} prop choices. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: override lodash-es to 4.18.0+ to resolve audit vulnerabilities mermaid (transitive dep via streamdown) pins lodash-es to 4.17.23 which has known Code Injection and Prototype Pollution vulnerabilities. Override to >=4.18.0 which is outside the vulnerable range (<=4.17.23). mermaid is tree-shaken and never loaded at runtime. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> --------- Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
quiet-node
added a commit
that referenced
this pull request
Apr 10, 2026
#17) * feat: replace react-markdown with streamdown for jitter-free streaming react-markdown re-parses the entire accumulated text on every token, producing different DOM trees for incomplete markdown and causing chat bubble height to oscillate during fast streaming. Streamdown (by Vercel) splits content into blocks and memoizes completed ones, so only the last in-progress block re-renders on each token. - Swap react-markdown + remark-gfm for streamdown - Add isStreaming prop through ChatBubble → MarkdownRenderer - Remove failed height-ratchet approach from ConversationView - Update tests for Streamdown's element rendering conventions Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: restore functional links and update security documentation Code review found four issues: 1. Links rendered as dead <button> elements (linkSafety modal was intercepting). Disable linkSafety so Streamdown renders native <a> tags with href, target="_blank", and rel="noopener noreferrer". Restore the href assertion in the link test. 2. CLAUDE.md architecture section said "DOMPurify sanitization" but DOMPurify was never used. Update to reflect actual renderer (Streamdown with rehype-sanitize). 3. Security rationale JSDoc was deleted in the streamdown migration. Add documentation of Streamdown's XSS protection model. 4. Add inline comments explaining controls={false} and linkSafety={{ enabled: false }} prop choices. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: override lodash-es to 4.18.0+ to resolve audit vulnerabilities mermaid (transitive dep via streamdown) pins lodash-es to 4.17.23 which has known Code Injection and Prototype Pollution vulnerabilities. Override to >=4.18.0 which is outside the vulnerable range (<=4.17.23). mermaid is tree-shaken and never loaded at runtime. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> --------- Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
quiet-node
added a commit
that referenced
this pull request
Apr 11, 2026
#17) * feat: replace react-markdown with streamdown for jitter-free streaming react-markdown re-parses the entire accumulated text on every token, producing different DOM trees for incomplete markdown and causing chat bubble height to oscillate during fast streaming. Streamdown (by Vercel) splits content into blocks and memoizes completed ones, so only the last in-progress block re-renders on each token. - Swap react-markdown + remark-gfm for streamdown - Add isStreaming prop through ChatBubble → MarkdownRenderer - Remove failed height-ratchet approach from ConversationView - Update tests for Streamdown's element rendering conventions Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: restore functional links and update security documentation Code review found four issues: 1. Links rendered as dead <button> elements (linkSafety modal was intercepting). Disable linkSafety so Streamdown renders native <a> tags with href, target="_blank", and rel="noopener noreferrer". Restore the href assertion in the link test. 2. CLAUDE.md architecture section said "DOMPurify sanitization" but DOMPurify was never used. Update to reflect actual renderer (Streamdown with rehype-sanitize). 3. Security rationale JSDoc was deleted in the streamdown migration. Add documentation of Streamdown's XSS protection model. 4. Add inline comments explaining controls={false} and linkSafety={{ enabled: false }} prop choices. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: override lodash-es to 4.18.0+ to resolve audit vulnerabilities mermaid (transitive dep via streamdown) pins lodash-es to 4.17.23 which has known Code Injection and Prototype Pollution vulnerabilities. Override to >=4.18.0 which is outside the vulnerable range (<=4.17.23). mermaid is tree-shaken and never loaded at runtime. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> --------- Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
This was referenced Apr 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
react-markdown+remark-gfmwithstreamdown(Vercel's streaming-aware markdown renderer) to eliminate chat bubble height jitter during LLM token streamingisStreamingprop throughChatBubble->MarkdownRendererto toggle between streaming and static render modes<a>withhref,target="_blank", andrel="noopener noreferrer"Test plan
bun run test)bun run test:coverage)bun run validate-build)href,rel, andtargetassertions🤖 Generated with Claude Code