Parse markdown incrementally as it arrives from LLM outputs using streaming mode
Streaming mode allows you to parse markdown incrementally as new content arrives, making it ideal for processing LLM outputs in real-time. The parser buffers incomplete blocks and only emits finalized, stable nodes.
import { MarkdownParser } from "markdown-parser";const parser = new MarkdownParser();
2
Parse the first chunk
// First chunk arrivesconst nodes1 = parser.parse("# Hello World\nThis", { stream: true });console.log(nodes1);// [{ type: "heading", level: 1, children: [{ type: "text", text: "Hello World" }] }]// The paragraph is NOT emitted - it's still open
3
Continue with more chunks
// More content arrivesconst nodes2 = parser.parse(" is a paragraph\n\nThis is another paragraph.", { stream: true });console.log(nodes2);// [{ type: "paragraph", children: [{ type: "text", text: "This is a paragraph" }] }]// First paragraph is now closed, but second one remains open
4
Finalize remaining blocks
// Close all remaining open blocksconst nodes3 = parser.parse("", { stream: false });console.log(nodes3);// [{ type: "paragraph", children: [{ type: "text", text: "This is another paragraph." }] }]
The CommonMark spec allows link reference definitions to appear after their usage:
const parser = new MarkdownParser();// Link reference before definitionlet nodes = parser.parse("[link][ref]\n\n", { stream: true });console.log(nodes);// Link might not resolve yet if definition comes later// Definition arrives in later chunknodes = parser.parse("[ref]: https://example.com\n", { stream: true });// Parser resolves references in finalized blocks
When streaming, links may initially appear unresolved if their reference definition arrives in a later chunk.
Don’t reuse parser instances across independent streams:
// ✅ Good: New parser for each streamconst parser1 = new MarkdownParser();processStream1(parser1);const parser2 = new MarkdownParser();processStream2(parser2);// ❌ Bad: Reusing parserconst parser = new MarkdownParser();processStream1(parser);processStream2(parser); // State is mixed!
Buffer small chunks
For very small chunks, consider buffering to reduce parser overhead:
const parser = new MarkdownParser();// All blocks finalized immediatelyconst nodes = parser.parse("# Title\nIncomplete paragraph");// Returns both heading AND paragraph (finalized)