Zero-dep streaming markdown for the browser. A Rust→WASM core with one pooled Web Worker per stream, incremental parse with speculative closure for mid-stream constructs, and stable block identities so unchanged blocks never re-reconcile.
Wire each LLM stream to a FluxClient and the markdown renders incrementally
off the main thread, block by block — so many concurrent streams render
without melting the UI thread. 100% CommonMark 0.31 + GFM.
Live demo · Full docs & API → · Changelog
npm i flux-md
import { FluxMarkdown } from "flux-md/react";
// `stream` is an AsyncIterable<string> (SSE deltas), a Response, or a ReadableStream
<FluxMarkdown stream={stream} />;
renderToString cleanly on the server across React,
Vue, Solid, and Svelte; the worker is created lazily on the client.block.data channel (opt-in, default off) — tables, headings,
code, math, and lists are exposed as typed, streaming data on
block.kind.data, so you build toolbars (sort/filter/CSV), tables of contents,
charts, and copy buttons from data — no HTML re-parsing, no AST tree to walk.<flux-markdown> Web Component, and a vanilla DOM mount.See the package README for the full API,
per-stream config, framework bindings, security model, and scaling helpers
(virtualize, stickToBottom).
| Path | What |
|---|---|
packages/flux-md |
The published npm package — TS client + renderers, and the full docs. |
crates/flux-md-core |
The Rust parser/renderer compiled to WASM (built on demand; not committed). |
web |
The live demo / playground (md.hsingh.app). |
bun install
bun run build:wasm # compile the Rust core → WASM
cd packages/flux-md && bun test
CI enforces the CommonMark 652/652 + GFM conformance floors, the JS test suite, a fresh-process SSR cold-import check, and that the published tarball ships the WASM.