AI interfaces stream text token by token. Users see responses build in real-time, character by character. This creates a unique rendering challenge: the markdown is incomplete until the stream finishes, and the content updates dozens of times per second.
StreamingMarkdown solves this problem. It handles the specific challenges of rendering AI-streamed markdown: completing unterminated syntax, memoizing blocks for performance, validating links for security, and rendering everything with KookieUI components.
Rendering streamed markdown sounds simple until you try it:
Here's some **bold text without the closing **. Standard markdown parsers break or show raw syntax.Teams building AI products hit these problems immediately. Most solutions involve custom markdown parsing logic, debounced rendering, or accepting visual glitches during streaming. None of these are satisfying.
StreamingMarkdown handles the markdown rendering layer for AI-streamed content. It's built specifically for the streaming use case while remaining useful for static markdown.
Unterminated syntax completion. The completeUnterminatedMarkdown utility scans content and closes any open markdown syntax. Bold, italic, code, links, headings, code fences, all get properly terminated so the parser can handle them.
tsBlock-level memoization. Instead of re-rendering the entire document on every update, StreamingMarkdown parses content into blocks and memoizes each independently. When a new token arrives, only the affected block re-renders. Earlier blocks stay cached.
tsSecurity hardening. Uses harden-react-markdown to validate all links and images. Configurable allowed protocols (https, http, mailto, tel). Validates against a security origin. Blocks javascript: URLs and other attack vectors.
GitHub Flavored Markdown. Full GFM support via remarkGfm: tables, strikethrough, autolinks, task lists. The syntax users expect from GitHub and other platforms.
KookieUI integration. Every markdown element renders as a KookieUI component. Headings use the Heading component with proper sizes. Code blocks use CodeBlock with syntax highlighting. Tables use the Table component. The output matches your design system.
StreamingMarkdown accepts content, an ID for memoization, and an options object:
The rendering pipeline:
Each step is optimized for the streaming case. Syntax completion runs incrementally. Block parsing uses the provided lexer for accurate splitting. Memoization prevents unnecessary work. The result is smooth rendering even at high token rates.
Two spacing modes handle different contexts:
Spacious (default) works for documentation and articles. Generous margins between sections, clear visual hierarchy, comfortable reading experience.
Compact works for chat interfaces. Tighter spacing that fits conversational context, reduced margins, denser information display.
tsStreamingMarkdown exports utilities for custom implementations:
completeUnterminatedMarkdown(content: string): string Completes unterminated syntax at the end of content. Handles headings, bold, italic, strikethrough, inline code, code fences, links, lists, and blockquotes.
parseMarkdownIntoBlocks(content: string, parser?: Function): string[] Splits content into blocks using the provided parser. Returns raw content strings ready for independent rendering.
createMarkdownComponents(options: MarkdownComponentOptions): Components Creates the component mapping for react-markdown. Customize spacing, code blocks, images, and more.
StreamingMarkdown follows Kookie Blocks principles:
Built on Kookie UI. Every element renders as a KookieUI component. The output matches your design system without additional styling.
Patterns, not parts. StreamingMarkdown encodes the complete pattern for rendering AI-streamed content. Syntax completion, memoization, security, and styling are all coordinated.
Performance by default. Block memoization is enabled by default. The component does the right thing without configuration.
Composable. Use the full component or import individual utilities. Integrate with your existing markdown setup or replace it entirely.
StreamingMarkdown powers the markdown rendering in Kookie AI. Every AI response streams through this component.
Before StreamingMarkdown, we had custom logic scattered across the chat interface: syntax completion hacks, manual memoization attempts, inconsistent styling between message types. The rendering worked but wasn't maintainable.
With StreamingMarkdown in Kookie Blocks:
The component handles thousands of messages daily. It's stable, fast, and maintainable.
StreamingMarkdown handles the core use case well. Areas I'm developing:
Enhanced code blocks. Better language detection, copy button integration, line highlighting. Code is a significant part of AI responses and deserves attention.
Math support. LaTeX and KaTeX rendering for technical and scientific content. Many AI use cases involve mathematical notation.
Custom block types. Extensible block system for tool calls, citations, and other structured content that AI produces beyond standard markdown.
Streaming indicators. Visual feedback for blocks still receiving content versus completed blocks. Users should understand what's still generating.
AI products need markdown rendering that handles streaming gracefully. The content arrives incomplete, updates constantly, and needs to look right throughout. StreamingMarkdown encodes this pattern in Kookie Blocks.
Teams get a tested, performant, secure component that handles the complex details. That lets them focus on the AI experience rather than rendering infrastructure. That's the value StreamingMarkdown brings to Kookie Blocks.