☁️ The fastest HTML to markdown convertor on GitHub. Optimized for LLMs and supports streaming.
See the code☁️ Convert HTML to Markdown for LLMs, with Rust and JavaScript engines and streaming support.
[!TIP] 🎉 Upgrading to Mdream v2? Follow Migrating to v2.
|
Made possible by my Sponsor Program 💖 Follow me @harlan_zw 🐦 • Join Discord for help |
Mdream converts HTML to Markdown for LLM input. The benchmarks compare conversion speed and token counts with other libraries.
Use the integration packages to generate Markdown pages and llms.txt files from your site.
Choose a package for your runtime or build tool:
| Package | Description |
|---|---|
| Rust NAPI engine + WASM for edge. Performance-first, declarative config. Includes CLI. | |
| Pure JS engine. Tree-shakable formats and plugins. Zero native dependencies. | |
Site-wide crawler to generate llms.txt artifacts from entire websites | |
Pre-built Docker images: a tiny core HTML-to-Markdown converter and a crawl image with Playwright Chrome | |
Generate automatic .md for your own Vite sites | |
Generate .md and llms.txt artifacts from your static .html output | |
| Native Rust crate with CLI. Zero dependencies, streaming support. Available on crates.io | |
| Use mdream directly in browsers via unpkg/jsDelivr without any build step |
Pipe any HTML into mdream to get clean, LLM-ready Markdown:
curl -s https://en.wikipedia.org/wiki/Markdown | npx mdream --preset minimal
Fetch the Markdown Wikipedia page, then convert it with the minimal preset:
curl -s https://en.wikipedia.org/wiki/Markdown \
| npx mdream --origin https://en.wikipedia.org --preset minimal \
| tee streaming.md
The --origin flag resolves relative image and link paths.
Want to make it look nice? Use glow.
curl -s https://en.wikipedia.org/wiki/Markdown \
| npx mdream --origin https://en.wikipedia.org --preset minimal \
| glow
Convert a local HTML file. The tee command saves the Markdown and displays it in the terminal.
cat index.html \
| npx mdream --preset minimal \
| tee streaming.md
Want to make it look nice? Use glow.
cat index.html \
| npx mdream --preset minimal \
| glow
Pipe web content straight into Claude, GPT, or any LLM CLI:
# Single page → Claude
curl -s https://react.dev/learn | npx mdream --origin https://react.dev --preset minimal \
| claude -p "explain the key concepts on this page"
# Crawl entire docs → summarize
npx @mdream/crawl "https://nuxt.com/docs/getting-started/**"
cat output/llms-full.txt | claude -p "write a getting started guide from these docs"
# Compare two frameworks
diff <(curl -s https://vuejs.org/guide/introduction | npx mdream --preset minimal) \
<(curl -s https://react.dev/learn | npx mdream --preset minimal) \
| claude -p "compare these two frameworks based on their intro docs"
# JavaScript/SPA sites (React, Vue, Angular)
npm install @mdream/crawl crawlee playwright
npx playwright install chromium
npx @mdream/crawl https://spa-site.com --driver playwright
cat output/llms-full.txt | claude -p "what features does this app have"
Generate llms.txt to help AI tools understand your site:
# Static sites
npx @mdream/crawl https://yoursite.com
# JavaScript/SPA sites (React, Vue, Angular)
npm install @mdream/crawl crawlee playwright
npx playwright install chromium
npx @mdream/crawl https://spa-site.com --driver playwright
Outputs:
output/llms.txt - Optimized for LLM consumptionoutput/llms-full.txt - Complete content with metadataoutput/<page>.md - Individual Markdown files, preserving each URL pathCrawl websites and generate embeddings for vector databases. Install the packages and configure an AI Gateway API key:
pnpm add @mdream/crawl @mdream/js ai
export AI_GATEWAY_API_KEY="your-api-key"
import { crawlAndGenerate } from '@mdream/crawl'
import { withMinimalPreset } from '@mdream/js/preset/minimal'
import { htmlToMarkdownSplitChunks } from '@mdream/js/splitter'
import { embed } from 'ai'
const embeddings: { url: string, title: string, content: string, embedding: number[] }[] = []
await crawlAndGenerate({
urls: ['https://example.com'],
output: './output',
hooks: {
'crawl:page': async ({ url, html, title, origin }) => {
const chunks = htmlToMarkdownSplitChunks(html, withMinimalPreset({
chunkSize: 1000,
chunkOverlap: 200,
origin,
}))
for (const chunk of chunks) {
const { embedding } = await embed({ model: 'openai/text-embedding-3-small', value: chunk.content })
embeddings.push({ url, title, content: chunk.content, embedding })
}
},
},
})
// Save to vector database: await saveToVectorDB(embeddings)
Pull headers, images, or other elements during conversion:
import { htmlToMarkdown } from 'mdream'
const headers = []
const images = []
htmlToMarkdown(html, {
extraction: {
'h1, h2, h3': el => headers.push(el.textContent),
'img[src]': el => images.push({ src: el.attributes.src, alt: el.attributes.alt }),
},
})
Pass clean: true to remove empty links, tracking parameters, and other unwanted output.
The minimal preset enables all cleanup rules by default.
import { htmlToMarkdown } from 'mdream'
// All clean features enabled
htmlToMarkdown(html, { clean: true })
// Or selective features
htmlToMarkdown(html, {
clean: {
emptyLinks: true, // Strip #, javascript: links
emptyLinkText: true, // Drop [](url) links with no text
emptyImages: true, // Strip  with no alt text
redundantLinks: true, // [url](url) → url
selfLinkHeadings: true, // ## [Title](#title) → ## Title
fragments: true, // Strip broken #anchor links
urls: true, // Strip utm_*, fbclid tracking params
}
})
pnpm add mdream
[!TIP] Using an AI agent? Get the mdream Skill on skilld.dev/gh/harlan-zw/mdream.
In Node.js, mdream loads native bindings that must remain outside the bundle.
If your bundler fails to resolve mdream, mark it as external:
Next.js / Turbopack:
// next.config.js
const nextConfig = {
serverExternalPackages: ['mdream'],
}
Webpack / other bundlers:
externals: ['mdream']
[!TIP]
@mdream/jshas zero native dependencies and works with all bundlers without configuration.
[!TIP] Using Vite?
@mdream/vitehandles this automatically.
import { htmlToMarkdown } from 'mdream'
// Rust NAPI engine in Node.js, WASM in edge runtimes
const markdown = htmlToMarkdown('<h1>Hello World</h1>')
console.log(markdown) // # Hello World
const text = htmlToMarkdown('<h1>Hello <strong>World</strong></h1>', {
format: 'text',
})
console.log(text) // Hello World
const html = htmlToMarkdown('<h1>Hello <strong>World</strong></h1>', {
format: 'html',
})
console.log(html) // <h1 id="hello-world">Hello <strong>World</strong></h1>
import { streamHtmlToMarkdown } from 'mdream'
const response = await fetch('https://en.wikipedia.org/wiki/Markdown')
for await (const chunk of streamHtmlToMarkdown(response.body, {
origin: 'https://en.wikipedia.org',
minimal: true,
})) {
process.stdout.write(chunk)
}
In a browser bundle, import htmlToMarkdown from mdream/browser and await it. The result is a string.
See the mdream docs for complete details.
Use the guide for each package:
mdream: browser imports and returns, Web Workers, minimal filtering, and page titles.@mdream/js: format imports, plugin arrays, cleanup, and callback changes.@mdream/crawl: renamed options, shared defaults, and the mdream-crawl command.The JS guide also covers upgrades from 2.0.0-beta.1.
That release used a different plugin API and format option.
Need something that works in the browser or an edge runtime? Use Mdream.
The @mdream/crawl package fetches site pages and converts them with mdream.
# Interactive
npx @mdream/crawl
# Simple
npx @mdream/crawl https://harlanzw.com
# Glob patterns
npx @mdream/crawl "https://nuxt.com/docs/getting-started/**"
# Get help
npx @mdream/crawl -h
Two images for two jobs:
# core — convert HTML to Markdown (native Rust binary, ~600KB, no Node)
curl -s https://example.com | docker run -i --rm harlanzw/mdream:core --origin https://example.com
# crawl — crawl a site / generate llms.txt (Playwright Chrome included)
docker run harlanzw/mdream:crawl "https://site.com/docs/**"
docker run harlanzw/mdream:crawl spa-site.com --driver playwright
Available Images (Docker Hub harlanzw/mdream, also on ghcr.io/harlan-zw/mdream):
:core - v2 native Rust HTML-to-Markdown converter (stdin → stdout):crawl - v2 site crawler with Playwright Chrome:latest - alias of :crawlSee DOCKER.md for complete usage, configuration, and building instructions.
pnpm add @mdream/action
See the GitHub Actions README for usage and configuration.
pnpm install @mdream/vite
See the Vite README for usage and configuration.
pnpm add @mdream/nuxt
See the Nuxt Module README for usage and configuration.
Load mdream from a CDN without a build step.
The script includes the WASM binary and initializes it on load.
Like mdream/browser, htmlToMarkdown() returns Promise<string>:
<script src="https://unpkg.com/mdream/dist/iife.js"></script>
<script>
window.mdream.htmlToMarkdown('<h1>Hello</h1><p>World</p>')
.then(markdown => console.log(markdown)) // # Hello\n\nWorld
</script>
CDN Options:
https://unpkg.com/mdream/dist/iife.jshttps://cdn.jsdelivr.net/npm/mdream/dist/iife.jsPure JS comparison. mdream uses no plugins, Turndown uses GFM plugin for equivalent table/strikethrough support.
| Input | mdream | Turndown | node-html-markdown | rehype-remark |
|---|---|---|---|---|
| 166 KB | 3.26ms | 11.26ms (3.5x) | 14.31ms (4.4x) | 35.19ms (10.8x) |
| 420 KB | 6.38ms | 13.63ms (2.1x) | 17.11ms (2.7x) | 62.10ms (9.7x) |
| 1.8 MB | 57.2ms | 264.3ms (4.6x) | 26,072ms (456x) | 826.7ms (14.5x) |
All crates compiled with opt-level=3, LTO, and single codegen unit.
| Input | mdream | htmd | html2md | html2md-rs | mdka | html_to_markdown |
|---|---|---|---|---|---|---|
| 166 KB | 0.34ms | 2.13ms (6.3x) | 2.71ms (8.0x) | panicked | 2.65ms (7.8x) | 1.72ms (5.1x) |
| 420 KB | 0.41ms | 3.50ms (8.6x) | 4.25ms (10.4x) | 1.54ms (3.8x) | 3.56ms (8.7x) | 2.72ms (6.7x) |
| 1.8 MB | 5.20ms | 34.4ms (6.6x) | >30s | 35.5ms (6.8x) | 37.6ms (7.2x) | 28.5ms (5.5x) |
For Node.js apps that need native speed. Includes N-API overhead.
| Input | mdream (rust) | html-to-markdown (rust) |
|---|---|---|
| 166 KB | 0.52ms | 3.94ms (7.6x) |
| 420 KB | 0.76ms | 7.48ms (9.8x) |
| 1.8 MB | 7.14ms | 82.9ms (11.6x) |
End-to-end cat file | tool > /dev/null via hyperfine. Includes process startup overhead (~20ms for Node.js, ~1ms for Go/Rust).
| Input | mdream (Rust) | mdream (Node.js) | html2markdown (Go) |
|---|---|---|---|
| 166 KB | 1.4ms | 26.9ms | 4.9ms |
| 420 KB | 2.1ms | 24.3ms | 5.6ms |
| 1.8 MB | 10.1ms | 34.8ms | 75.2ms (7.5x) |
In these results, mdream's Rust CLI is 2.6-7.5x faster than Go html2markdown. On the 1.8 MB file, the Node.js CLI is 2.2x faster, including its startup time. For conversion speed without startup time, see the JS and Rust tables above.
Both mdream engines accept HTML streams and yield Markdown as conversion progresses. Join the output pieces to get the same result as one-shot conversion.
With minimal: true, mdream produces up to 92% fewer tokens than raw HTML in these measured pages.
| Page (HTML tokens) | mdream minimal | Turndown | node-html-markdown |
|---|---|---|---|
| Wikipedia (21K) | 6,101 (-71%) | 10,435 (-50%) | 10,176 (-52%) |
| GitHub Docs (62K) | 5,006 (-92%) | 43,983 (-30%) | 8,758 (-86%) |
| Wikipedia XL (194K) | 152,425 (-21%) | 195,978 (+1%) | 283,136 (+46%) |
Benchmarks run on real-world HTML using Vitest bench. See full methodology and reproduction steps.
Using a coding agent? Give it the v1 to v2 migration prompt. The prompt covers package detection, code changes, and verification.
These docs cover v2. For an earlier release, read the docs at its release tag. The migration guides also cover upgrades from an earlier v2 beta.
| Package | Migration guide |
|---|---|
mdream, browser, edge, and CDN | Imports, returns, options, and workers |
@mdream/js | Formats, plugins, cleanup, and splitter changes |
@mdream/crawl | Option renames and crawl defaults |
@mdream/vite | Converter imports and plugin order |
@mdream/nuxt | Preset defaults and runtime options |
@mdream/action | Workflow steps and inputs |
| Rust crate | Types, features, and splitter options |
Before deploying v2, convert sample pages with your application's options and compare the output.
If you use the JS splitter, rebuild stored chunks and embeddings before your application reads them.
For your first v2 crawl, set depth, maxPages, and artifacts to limit pages and output files.
Licensed under the MIT license.
(top 24 of 28)
5,152 followers · starred Jul 2025
3,979 followers · starred Feb 2026
6,580 followers · starred Feb 2026
1,153 followers · starred Dec 2025
☁️ The fastest HTML to markdown convertor on GitHub. Optimized for LLMs and supports streaming.
See the code☁️ Convert HTML to Markdown for LLMs, with Rust and JavaScript engines and streaming support.
[!TIP] 🎉 Upgrading to Mdream v2? Follow Migrating to v2.
|
Made possible by my Sponsor Program 💖 Follow me @harlan_zw 🐦 • Join Discord for help |
Mdream converts HTML to Markdown for LLM input. The benchmarks compare conversion speed and token counts with other libraries.
Use the integration packages to generate Markdown pages and llms.txt files from your site.
Choose a package for your runtime or build tool:
| Package | Description |
|---|---|
| Rust NAPI engine + WASM for edge. Performance-first, declarative config. Includes CLI. | |
| Pure JS engine. Tree-shakable formats and plugins. Zero native dependencies. | |
Site-wide crawler to generate llms.txt artifacts from entire websites | |
Pre-built Docker images: a tiny core HTML-to-Markdown converter and a crawl image with Playwright Chrome | |
Generate automatic .md for your own Vite sites | |
Generate .md and llms.txt artifacts from your static .html output | |
| Native Rust crate with CLI. Zero dependencies, streaming support. Available on crates.io | |
| Use mdream directly in browsers via unpkg/jsDelivr without any build step |
Pipe any HTML into mdream to get clean, LLM-ready Markdown:
curl -s https://en.wikipedia.org/wiki/Markdown | npx mdream --preset minimal
Fetch the Markdown Wikipedia page, then convert it with the minimal preset:
curl -s https://en.wikipedia.org/wiki/Markdown \
| npx mdream --origin https://en.wikipedia.org --preset minimal \
| tee streaming.md
The --origin flag resolves relative image and link paths.
Want to make it look nice? Use glow.
curl -s https://en.wikipedia.org/wiki/Markdown \
| npx mdream --origin https://en.wikipedia.org --preset minimal \
| glow
Convert a local HTML file. The tee command saves the Markdown and displays it in the terminal.
cat index.html \
| npx mdream --preset minimal \
| tee streaming.md
Want to make it look nice? Use glow.
cat index.html \
| npx mdream --preset minimal \
| glow
Pipe web content straight into Claude, GPT, or any LLM CLI:
# Single page → Claude
curl -s https://react.dev/learn | npx mdream --origin https://react.dev --preset minimal \
| claude -p "explain the key concepts on this page"
# Crawl entire docs → summarize
npx @mdream/crawl "https://nuxt.com/docs/getting-started/**"
cat output/llms-full.txt | claude -p "write a getting started guide from these docs"
# Compare two frameworks
diff <(curl -s https://vuejs.org/guide/introduction | npx mdream --preset minimal) \
<(curl -s https://react.dev/learn | npx mdream --preset minimal) \
| claude -p "compare these two frameworks based on their intro docs"
# JavaScript/SPA sites (React, Vue, Angular)
npm install @mdream/crawl crawlee playwright
npx playwright install chromium
npx @mdream/crawl https://spa-site.com --driver playwright
cat output/llms-full.txt | claude -p "what features does this app have"
Generate llms.txt to help AI tools understand your site:
# Static sites
npx @mdream/crawl https://yoursite.com
# JavaScript/SPA sites (React, Vue, Angular)
npm install @mdream/crawl crawlee playwright
npx playwright install chromium
npx @mdream/crawl https://spa-site.com --driver playwright
Outputs:
output/llms.txt - Optimized for LLM consumptionoutput/llms-full.txt - Complete content with metadataoutput/<page>.md - Individual Markdown files, preserving each URL pathCrawl websites and generate embeddings for vector databases. Install the packages and configure an AI Gateway API key:
pnpm add @mdream/crawl @mdream/js ai
export AI_GATEWAY_API_KEY="your-api-key"
import { crawlAndGenerate } from '@mdream/crawl'
import { withMinimalPreset } from '@mdream/js/preset/minimal'
import { htmlToMarkdownSplitChunks } from '@mdream/js/splitter'
import { embed } from 'ai'
const embeddings: { url: string, title: string, content: string, embedding: number[] }[] = []
await crawlAndGenerate({
urls: ['https://example.com'],
output: './output',
hooks: {
'crawl:page': async ({ url, html, title, origin }) => {
const chunks = htmlToMarkdownSplitChunks(html, withMinimalPreset({
chunkSize: 1000,
chunkOverlap: 200,
origin,
}))
for (const chunk of chunks) {
const { embedding } = await embed({ model: 'openai/text-embedding-3-small', value: chunk.content })
embeddings.push({ url, title, content: chunk.content, embedding })
}
},
},
})
// Save to vector database: await saveToVectorDB(embeddings)
Pull headers, images, or other elements during conversion:
import { htmlToMarkdown } from 'mdream'
const headers = []
const images = []
htmlToMarkdown(html, {
extraction: {
'h1, h2, h3': el => headers.push(el.textContent),
'img[src]': el => images.push({ src: el.attributes.src, alt: el.attributes.alt }),
},
})
Pass clean: true to remove empty links, tracking parameters, and other unwanted output.
The minimal preset enables all cleanup rules by default.
import { htmlToMarkdown } from 'mdream'
// All clean features enabled
htmlToMarkdown(html, { clean: true })
// Or selective features
htmlToMarkdown(html, {
clean: {
emptyLinks: true, // Strip #, javascript: links
emptyLinkText: true, // Drop [](url) links with no text
emptyImages: true, // Strip  with no alt text
redundantLinks: true, // [url](url) → url
selfLinkHeadings: true, // ## [Title](#title) → ## Title
fragments: true, // Strip broken #anchor links
urls: true, // Strip utm_*, fbclid tracking params
}
})
pnpm add mdream
[!TIP] Using an AI agent? Get the mdream Skill on skilld.dev/gh/harlan-zw/mdream.
In Node.js, mdream loads native bindings that must remain outside the bundle.
If your bundler fails to resolve mdream, mark it as external:
Next.js / Turbopack:
// next.config.js
const nextConfig = {
serverExternalPackages: ['mdream'],
}
Webpack / other bundlers:
externals: ['mdream']
[!TIP]
@mdream/jshas zero native dependencies and works with all bundlers without configuration.
[!TIP] Using Vite?
@mdream/vitehandles this automatically.
import { htmlToMarkdown } from 'mdream'
// Rust NAPI engine in Node.js, WASM in edge runtimes
const markdown = htmlToMarkdown('<h1>Hello World</h1>')
console.log(markdown) // # Hello World
const text = htmlToMarkdown('<h1>Hello <strong>World</strong></h1>', {
format: 'text',
})
console.log(text) // Hello World
const html = htmlToMarkdown('<h1>Hello <strong>World</strong></h1>', {
format: 'html',
})
console.log(html) // <h1 id="hello-world">Hello <strong>World</strong></h1>
import { streamHtmlToMarkdown } from 'mdream'
const response = await fetch('https://en.wikipedia.org/wiki/Markdown')
for await (const chunk of streamHtmlToMarkdown(response.body, {
origin: 'https://en.wikipedia.org',
minimal: true,
})) {
process.stdout.write(chunk)
}
In a browser bundle, import htmlToMarkdown from mdream/browser and await it. The result is a string.
See the mdream docs for complete details.
Use the guide for each package:
mdream: browser imports and returns, Web Workers, minimal filtering, and page titles.@mdream/js: format imports, plugin arrays, cleanup, and callback changes.@mdream/crawl: renamed options, shared defaults, and the mdream-crawl command.The JS guide also covers upgrades from 2.0.0-beta.1.
That release used a different plugin API and format option.
Need something that works in the browser or an edge runtime? Use Mdream.
The @mdream/crawl package fetches site pages and converts them with mdream.
# Interactive
npx @mdream/crawl
# Simple
npx @mdream/crawl https://harlanzw.com
# Glob patterns
npx @mdream/crawl "https://nuxt.com/docs/getting-started/**"
# Get help
npx @mdream/crawl -h
Two images for two jobs:
# core — convert HTML to Markdown (native Rust binary, ~600KB, no Node)
curl -s https://example.com | docker run -i --rm harlanzw/mdream:core --origin https://example.com
# crawl — crawl a site / generate llms.txt (Playwright Chrome included)
docker run harlanzw/mdream:crawl "https://site.com/docs/**"
docker run harlanzw/mdream:crawl spa-site.com --driver playwright
Available Images (Docker Hub harlanzw/mdream, also on ghcr.io/harlan-zw/mdream):
:core - v2 native Rust HTML-to-Markdown converter (stdin → stdout):crawl - v2 site crawler with Playwright Chrome:latest - alias of :crawlSee DOCKER.md for complete usage, configuration, and building instructions.
pnpm add @mdream/action
See the GitHub Actions README for usage and configuration.
pnpm install @mdream/vite
See the Vite README for usage and configuration.
pnpm add @mdream/nuxt
See the Nuxt Module README for usage and configuration.
Load mdream from a CDN without a build step.
The script includes the WASM binary and initializes it on load.
Like mdream/browser, htmlToMarkdown() returns Promise<string>:
<script src="https://unpkg.com/mdream/dist/iife.js"></script>
<script>
window.mdream.htmlToMarkdown('<h1>Hello</h1><p>World</p>')
.then(markdown => console.log(markdown)) // # Hello\n\nWorld
</script>
CDN Options:
https://unpkg.com/mdream/dist/iife.jshttps://cdn.jsdelivr.net/npm/mdream/dist/iife.jsPure JS comparison. mdream uses no plugins, Turndown uses GFM plugin for equivalent table/strikethrough support.
| Input | mdream | Turndown | node-html-markdown | rehype-remark |
|---|---|---|---|---|
| 166 KB | 3.26ms | 11.26ms (3.5x) | 14.31ms (4.4x) | 35.19ms (10.8x) |
| 420 KB | 6.38ms | 13.63ms (2.1x) | 17.11ms (2.7x) | 62.10ms (9.7x) |
| 1.8 MB | 57.2ms | 264.3ms (4.6x) | 26,072ms (456x) | 826.7ms (14.5x) |
All crates compiled with opt-level=3, LTO, and single codegen unit.
| Input | mdream | htmd | html2md | html2md-rs | mdka | html_to_markdown |
|---|---|---|---|---|---|---|
| 166 KB | 0.34ms | 2.13ms (6.3x) | 2.71ms (8.0x) | panicked | 2.65ms (7.8x) | 1.72ms (5.1x) |
| 420 KB | 0.41ms | 3.50ms (8.6x) | 4.25ms (10.4x) | 1.54ms (3.8x) | 3.56ms (8.7x) | 2.72ms (6.7x) |
| 1.8 MB | 5.20ms | 34.4ms (6.6x) | >30s | 35.5ms (6.8x) | 37.6ms (7.2x) | 28.5ms (5.5x) |
For Node.js apps that need native speed. Includes N-API overhead.
| Input | mdream (rust) | html-to-markdown (rust) |
|---|---|---|
| 166 KB | 0.52ms | 3.94ms (7.6x) |
| 420 KB | 0.76ms | 7.48ms (9.8x) |
| 1.8 MB | 7.14ms | 82.9ms (11.6x) |
End-to-end cat file | tool > /dev/null via hyperfine. Includes process startup overhead (~20ms for Node.js, ~1ms for Go/Rust).
| Input | mdream (Rust) | mdream (Node.js) | html2markdown (Go) |
|---|---|---|---|
| 166 KB | 1.4ms | 26.9ms | 4.9ms |
| 420 KB | 2.1ms | 24.3ms | 5.6ms |
| 1.8 MB | 10.1ms | 34.8ms | 75.2ms (7.5x) |
In these results, mdream's Rust CLI is 2.6-7.5x faster than Go html2markdown. On the 1.8 MB file, the Node.js CLI is 2.2x faster, including its startup time. For conversion speed without startup time, see the JS and Rust tables above.
Both mdream engines accept HTML streams and yield Markdown as conversion progresses. Join the output pieces to get the same result as one-shot conversion.
With minimal: true, mdream produces up to 92% fewer tokens than raw HTML in these measured pages.
| Page (HTML tokens) | mdream minimal | Turndown | node-html-markdown |
|---|---|---|---|
| Wikipedia (21K) | 6,101 (-71%) | 10,435 (-50%) | 10,176 (-52%) |
| GitHub Docs (62K) | 5,006 (-92%) | 43,983 (-30%) | 8,758 (-86%) |
| Wikipedia XL (194K) | 152,425 (-21%) | 195,978 (+1%) | 283,136 (+46%) |
Benchmarks run on real-world HTML using Vitest bench. See full methodology and reproduction steps.
Using a coding agent? Give it the v1 to v2 migration prompt. The prompt covers package detection, code changes, and verification.
These docs cover v2. For an earlier release, read the docs at its release tag. The migration guides also cover upgrades from an earlier v2 beta.
| Package | Migration guide |
|---|---|
mdream, browser, edge, and CDN | Imports, returns, options, and workers |
@mdream/js | Formats, plugins, cleanup, and splitter changes |
@mdream/crawl | Option renames and crawl defaults |
@mdream/vite | Converter imports and plugin order |
@mdream/nuxt | Preset defaults and runtime options |
@mdream/action | Workflow steps and inputs |
| Rust crate | Types, features, and splitter options |
Before deploying v2, convert sample pages with your application's options and compare the output.
If you use the JS splitter, rebuild stored chunks and embeddings before your application reads them.
For your first v2 crawl, set depth, maxPages, and artifacts to limit pages and output files.
Licensed under the MIT license.
(top 24 of 28)
5,152 followers · starred Jul 2025
3,979 followers · starred Feb 2026
6,580 followers · starred Feb 2026
1,153 followers · starred Dec 2025