quikdown
June 14, 2026 · View on GitHub
Quikdown is a small, safe, bidirectional Markdown parser and editor for browser and Node.js apps — with rich fences, streaming, undo/redo, MCP tools, and a standalone airgapped build.
Try the Editor | Live Site | Examples | Downloads | Docs
Split-mode editor: Markdown source on the left, live preview on the right — with rendered Mermaid diagrams, MathJax equations, and syntax-highlighted code.
Why Quikdown?
- Small — ~15 KB parser, ~98 KB editor. Zero runtime dependencies.
- Safe by default — all HTML escaped, URL sanitization blocks
javascript:/vbscript:/ non-imagedata:URIs. Noeval, no dynamic regex. - Bidirectional — edit rendered HTML and get Markdown back. Round-trip preserves fences, tables, and formatting.
- Rich fences — Mermaid diagrams, Vega/Vega-Lite charts, MathJax equations, ABC music notation, GeoJSON maps, CSV/TSV/PSV tables, SVG, STL 3D models, syntax-highlighted code, and custom fences via the plugin API.
- Undo/redo — programmatic undo/redo lets LLMs and humans co-edit a shared document with full change reversibility.
- Offline standalone — a self-contained airgapped build (~7.7 MB / ~1 MB gzipped) bundles everything for air-gapped, regulated, or field environments. See companion app Quikleaf for an offline desktop app (with optional LLM support).
- LLM-friendly — stream Markdown tokens in, render incrementally, reverse agent mistakes with undo/redo. 24-tool MCP server for AI agents.
Why another Markdown parser?
Quikdown is a compact, embeddable parser/editor where Markdown remains the source of truth — for both humans and agents. It doesn't try to replace full CommonMark parsers or heavyweight editor stacks. It fills the gap for apps that need a fast, safe, self-contained Markdown render+edit surface with minimal footprint.
Modules
Nine modules — use only what you need:
| Module | Size | What it does |
|---|---|---|
| quikdown.js | ~15 KB | Markdown → HTML parser. XSS-safe, fence plugin callbacks, inline styles or CSS classes. |
| quikdown_bd.js | ~20 KB | Bidirectional: core parser + HTML → Markdown round-trip. |
| quikdown_edit.js | ~98 KB | Drop-in split-view editor. Undo/redo, toolbar, lazy-loaded fence plugins. |
| quikdown_edit_standalone.js | ~7.7 MB | Offline editor. All fence libraries bundled — no CDN required. Docs |
| quikdown_mcp.js | ~26 KB | MCP server. 24 tools for AI agents over JSON-RPC 2.0. |
| quikdown_ast/json/yaml/ast_html | ~5–8 KB each | AST companion libraries for structured output. |
Features
- Compact markdown parser — single-pass markdown to HTML. Handles headings, lists, tables, code blocks, inline formatting, task lists, autolinks, and lazy linefeeds.
- Bidirectional editing — edit the rendered HTML and get markdown back. Round-trip preserves formatting, fences, and tables.
- Drop-in editor — one
<div>, one import. Source, split, and preview modes. Undo/redo, toolbar, copy as rich text, light/dark/auto themes. - Fence plugins — code (highlight.js), Mermaid diagrams, MathJax equations, inline SVG, CSV/TSV/PSV tables, GeoJSON maps, STL 3D models, ABC music notation, Vega/Vega-Lite charts, raw HTML.
- XSS-safe — HTML entities escaped by default. URL sanitization blocks
javascript:,vbscript:, and non-imagedata:URIs. - Browser and Node.js — forward parser and bidirectional parser work in both. Quikdown Editor requires DOM.
- Zero runtime deps (core) — parser and bidirectional modules have no dependencies. The editor lazy-loads fence libraries (highlight.js, Mermaid, MathJax, ABCJS, Vega, etc.) from CDN on demand, or use the standalone offline bundle (~7.7 MB / ~1.0 MB gzipped, no CDN).
- TypeScript definitions — maintained
.d.tsfiles for all modules. - Inline styles or CSS classes — built-in light/dark themes, or bring your own CSS.
- Copy as rich text — copies the rendered preview to clipboard with images, tables, and rendered fences. Paste into Gmail, Word, Slack, Notion.
- Headless mode — run the editor without a toolbar. Wire
undo(),setTheme(),setMode()to your own UI. Rich API for controlling / selecting / manipulating markdown and rendered text with undo/redo support. - Structured output — parse markdown into AST, JSON, or YAML via companion libraries.
Security
All HTML is escaped by default. Only safe Markdown constructs become HTML:
const unsafe = '<script>alert("XSS")</script> **bold**';
const safe = quikdown(unsafe);
// <script>alert("XSS")</script> <strong>bold</strong>
- HTML escaped by default — no raw HTML passes through unless a fence plugin explicitly renders it.
- URL sanitization — blocks
javascript:,vbscript:, and non-imagedata:URIs in links and images. - Trusted fence path — fence plugin callbacks receive pre-extracted content and the declared language tag. The plugin decides what to render; untrusted content never reaches
innerHTMLwithout an explicit opt-in. The built-in editor uses DOMPurify for thehtmlfence. - No
eval, no dynamicRegExp— all regex patterns are static literals, verified free of catastrophic backtracking (ReDoS). - Static analysis — passes ESLint with eslint-plugin-security at error level with zero findings. Enforced in CI on every build.
For the full security model, see docs/security.md.
Installation
Quikdown is available via NPM and related unpkg and jsdelivr
NPM package
npm install quikdown
CDN using UNPKG
CDN (ES Modules):
<script type="module">
import quikdown from 'https://unpkg.com/quikdown/dist/quikdown.esm.min.js';
document.body.innerHTML = quikdown('# Hello World');
</script>
CDN (UMD):
<script src="https://unpkg.com/quikdown/dist/quikdown.umd.min.js"></script>
<script>
document.body.innerHTML = quikdown('# Hello World');
</script>
Quick Start
Quikdown is built in 3 versions. The smallest (quikdown) provides markdown to html conversion only. The next (quikdown_bd) provides markdown to html and html to markdown support. The lightweight editor quikdown_edit allows a bidirectional editor with lazy loading for common fences such as codeblocks, svg, and mermaid diagrams is also provided.
Markdown → HTML (quikdown.js)
// Basic conversion
const html = quikdown('# Hello World',
{inline_styles: true} // Use inline styles, more options in API docs
);
document.body.innerHTML = html;
Bidirectional Markdown ↔ HTML (quikdown_bd.js)
// Convert with source tracking
const htmlString = quikdown_bd(markdown, options);
// Convert HTML back to Markdown
const markdown = quikdown_bd.toMarkdown(htmlString);
Note: quikdown does not provide a generic html to markdown conversion but uses special tags and limited DOM parsing for HTML to markdown conversion. Standard markdown components such as headings, text styles, tables, quotes, etc are supported. For custom fences quikdown relies on its tag system or 3rd party handlers to provide reverse (html to md) conversion.
Editor (quikdown_edit.js)
const editor = new QuikdownEditor('#container', {
mode: 'split', // 'source', 'split', 'preview'
theme: 'auto', // 'light', 'dark', 'auto'
plugins: { highlightjs: true, mermaid: true } // built-in fence handlers, see API docs for custom plugins
});
editor.setMarkdown('# Content \nTo be quik or not to be.'); // provide default content
const content = editor.getMarkdown(); // get source content, see APIs for getting / setting HTML
AST Libraries (quikdown_ast, quikdown_json, quikdown_yaml)
Convert markdown to structured data formats for programmatic manipulation:
import quikdown_ast from 'quikdown/ast';
import quikdown_json from 'quikdown/json';
import quikdown_yaml from 'quikdown/yaml';
import quikdown_ast_html from 'quikdown/ast-html';
const markdown = '# Hello\n\nWorld **bold**';
// Markdown → AST object
const ast = quikdown_ast(markdown);
// Markdown → JSON string
const json = quikdown_json(markdown);
// Markdown → YAML string
const yaml = quikdown_yaml(markdown);
// AST/JSON/YAML → HTML
const html = quikdown_ast_html(ast); // or pass json/yaml string
The AST parsers are "forgiving" - they handle malformed markdown gracefully without throwing errors. See AST Documentation for the complete node type reference.
Note: The editor automatically lazy-loads plugin libraries from CDNs when needed:
- highlight.js - Loaded when code blocks are encountered and
highlightjs: true - mermaid - Loaded when mermaid diagrams are found and
mermaid: true - DOMPurify - Loaded when HTML fence blocks are rendered
- MathJax v3 - Loaded when
math,tex,latex, orkatexfence blocks are encountered (katexlang kept for backward compatibility) - ABCJS - Loaded when
abcormusicfence blocks are encountered (sheet music rendering) - Vega + Vega-Lite + Vega-Embed - Loaded when
vega,vega-lite, orvegalitefence blocks are encountered (data visualization)
This keeps the initial bundle small while providing rich functionality on-demand.
Integration
quikdown meets three common integration patterns. Pick the entry point that matches who drives the document:
| Audience | What you need | Entry point |
|---|---|---|
| Parse-only | Render markdown, AST/JSON/YAML, streaming HTML in your app | import quikdown from 'quikdown' — or quikdown/ast, quikdown/json, quikdown/yaml |
| File agents | An AI agent reads and writes .md / HTML files in a repo sandbox | Path A: npx quikdown-mcp --root=. — IDE editing, no browser |
| Doc copilot | Human edits in a live preview; agent drives the same buffer | Path B: node examples/mcp-doc-host/start-mcp.js — browser QuikdownEditor + Node MCP bridge |
Parse-only is the default: zero config, embed anywhere. File agents add MCP filesystem tools under a --root sandbox — best when the human stays in Cursor or VS Code. Doc copilot binds the full editor surface (regex, undo, rich render export) when the human works in a browser tab.
Browser demos and streaming patterns: docs/llm-integration.md. MCP setup and tool reference: docs/quikdown-mcp.md.
LLM and agent UIs
quikdown fits the model writes markdown, human sees rendered output loop:
| Pattern | Doc / demo |
|---|---|
| AI Canvas — chat + document editor | examples/ai-canvas — simulated or live LLM (BYOK) |
| Agent tool calling on editor | examples/llm-tool-editor |
| MCP doc copilot (Node + browser) | examples/mcp-doc-host — 24 MCP tools via WebSocket |
| Stream into QuikdownEditor | examples/llm-stream-editor |
| Stream tokens into HTML | integration-llm-stream |
| Chat bubbles + markdown | quikchat + integration example |
Overview: docs/llm-integration.md
MCP Server (AI Agent Integration)
quikdown includes an MCP server that lets AI agents parse markdown, convert between formats, read/write files, and (with a doc host) control the editor — all over JSON-RPC 2.0.
Two paths:
| Path | Human UI | Command |
|---|---|---|
| A — IDE (shipped) | Cursor / VS Code file editor; no browser | npx quikdown-mcp --root=. |
| B — Doc copilot (shipped) | Browser tab with QuikdownEditor; Node host bridges MCP | node examples/mcp-doc-host/start-mcp.js |
Path B is a Node launcher + browser window — you work in the browser; the agent drives the same doc via MCP through Node. Setup: examples/mcp-doc-host/README.md. Overview: Path A vs Path B.
npm install quikdown
npx quikdown-mcp --root=.
24 tools in three groups:
| Group | Tools | Activated by |
|---|---|---|
| Headless | markdown_to_html, html_to_markdown, markdown_stats, quikdown_info, markdown_to_ast, markdown_to_json | Always |
| Filesystem | read_file_info, read_file_lines, read_file_markdown, write_markdown_to_file, write_html_to_file | --root flag |
| Editor | read_editor, write_editor, find_regex, replace_regex, replace_text, extract_text, get_stats, get_html, undo, redo, load_file_to_editor, get_rendered, write_rendered_to_file | Editor binding |
Cursor (Path A — IDE + repo files):
{
"mcpServers": {
"quikdown": { "command": "npx", "args": ["quikdown-mcp", "--root=."] }
}
}
Cursor (Path B — doc copilot, from quikdown repo): run npm run build first, then:
{
"mcpServers": {
"quikdown-doc": {
"command": "node",
"args": ["examples/mcp-doc-host/start-mcp.js"]
}
}
}
Opens a browser tab with QuikdownEditor; you edit in the browser while the agent uses MCP. See examples/mcp-doc-host/README.md.
Claude Desktop (Path A):
{
"mcpServers": {
"quikdown": { "command": "npx", "args": ["quikdown-mcp", "--root=."] }
}
}
Programmatic:
import { createMcpServer } from 'quikdown/mcp';
const mcp = createMcpServer({ root: '.' });
const result = mcp.callTool('markdown_to_html', { markdown: '# Hello' });
Full documentation: docs/quikdown-mcp.md | MCP setup page
Other Configuration Options
quikdown supports built-in styles for a "batteries included" experience or you can bring your own CSS themes. Example css files are provided for basic light and dark themes to get started.
const html = quikdown(markdown, {
lazy_linefeeds: true, // Single newlines become <br>
inline_styles: false, // Use class based CSS instead of inline styles
fence_plugin: { // Custom code block processor (v1.1.0+ API)
render: myHandler // Function to render fence blocks
}
});
Styling Options
Inline styles: All formatting uses inline CSS
quikdown('**bold**', { inline_styles: true });
// <strong style="font-weight: bold;">bold</strong>
Class-based styling: Uses CSS classes (default)
quikdown('**bold**');
// <strong>bold</strong>
// Requires CSS: .quikdown strong { font-weight: bold; }
// see included dist/quikdown.light.css or quikdown.dark.css
Fence Plugins
Quikdown provides a callback for all fenced text such as code blocks, math, svg etc.
Handle code blocks with custom languages:
const fencePlugin = {
render: (code, language) => {
if (language === 'mermaid') {
// Process with mermaid library and return rendered diagram
const id = 'mermaid-' + Math.random().toString(36).substr(2, 9);
setTimeout(() => mermaid.render(id + '-svg', code).then(result => {
document.getElementById(id).innerHTML = result.svg;
}), 0);
return `<div id="${id}" class="mermaid">Loading diagram...</div>`;
}
// Return undefined for default handling
}
};
const html = quikdown(markdown, { fence_plugin: fencePlugin });
TypeScript Support
quikdown includes TypeScript definitions for better IDE support and type safety:
import quikdown, { QuikdownOptions, FencePlugin } from 'quikdown';
const fencePlugin: FencePlugin = {
render: (content: string, language: string) => {
return `<pre class="hljs ${language}">${content}</pre>`;
}
};
const options: QuikdownOptions = {
inline_styles: true,
fence_plugin: fencePlugin
};
const html: string = quikdown(markdown, options);
Supported Markdown
Text formatting: **bold**, *italic*, ~~strikethrough~~, `code`
Headings: # H1 through ###### H6
Lists:
- Unordered lists
- Ordered lists
- Task lists
Links: [text](url) and automatic URL detection
Code blocks:
console.log('syntax highlighting support via plugins');
Tables, blockquotes, horizontal rules - See documentation for complete syntax reference
API Reference
For complete API documentation, see docs/api-reference.md
Framework Integration
Works with React, Vue, Svelte, Angular. See Framework Integration Guide. For LLM/agent patterns (streaming, tool editor), see LLM Integration.
Streaming
Quikdown's parser is fast enough to re-parse the full buffer on every incoming chunk — no incremental parsing state to manage:
// Render-only: stream tokens into a div
let buffer = '';
for await (const chunk of llmStream) {
buffer += chunk;
previewEl.innerHTML = quikdown(buffer, { lazy_linefeeds: true });
}
// Editor: stream into QuikdownEditor
let buffer = '';
for await (const chunk of llmStream) {
buffer += chunk;
await editor.setMarkdown(buffer);
}
Incomplete fences are handled gracefully — they render as plain text until the closing fence arrives. See docs/llm-integration.md for production patterns (debouncing, undo grouping).
Undo / Redo
The editor maintains a configurable undo stack (default 100 states). Keyboard shortcuts work out of the box:
| Action | Shortcut |
|---|---|
| Undo | Ctrl+Z (Cmd+Z on Mac) |
| Redo | Ctrl+Shift+Z / Ctrl+Y |
const editor = new QuikdownEditor('#container', {
undoStackSize: 200, // max undo states (default 100)
showUndoRedo: true // show toolbar buttons
});
editor.undo(); // undo last change
editor.redo(); // redo
editor.canUndo(); // → boolean
editor.canRedo(); // → boolean
editor.clearHistory(); // wipe all history
This is especially useful for human + LLM collaborative editing: agents make mistakes, and users need reversible edits. Undo/redo is also available as an MCP tool in Path B. Full API: docs/quikdown-editor.md.
Standalone / Airgapped Build
The standalone editor bundles all fence libraries — no CDN, no network required:
# Download from GitHub Releases
# or build locally:
npm run build:standalone
<!-- Single file, works offline -->
<div id="editor"></div>
<script src="quikdown_edit_standalone.umd.min.js"></script>
<script>
const editor = new QuikdownEditor('#editor', { mode: 'split' });
editor.setMarkdown('# Works offline');
</script>
Use cases: air-gapped networks, regulated environments, field deployments, offline demos, Electron apps, embedded systems. See docs/standalone-editor.md for the full list of bundled libraries and build details. Pre-built zip available on GitHub Releases.
Comparison
| Quikdown | marked | markdown-it | ProseMirror + markdown | |
|---|---|---|---|---|
| Parser size | ~15 KB | ~40 KB | ~100 KB | ~200 KB+ |
| Editor included | Yes (~98 KB) | No | No | Yes (large stack) |
| XSS-safe by default | Yes | No (opt-in) | No (opt-in) | Depends on schema |
| Bidirectional | Yes | No | No | Yes |
| Streaming-friendly | Yes | Yes | Yes | Complex |
| Offline standalone | Yes (~7.7 MB all-in) | N/A | N/A | N/A |
| Runtime deps | 0 | 0 | 0 | Many |
| MCP server | 24 tools | No | No | No |
| CommonMark coverage | Subset | Full | Full | Full |
Quikdown intentionally trades full CommonMark coverage for a smaller, safer, more integrated package. If you need full spec compliance or a heavyweight collaborative editing framework, marked/markdown-it/ProseMirror are better choices.
What Quikdown Is Not
- Not a full CommonMark parser — reference-style links, footnotes, and definition lists are intentionally omitted for size and security.
- Not a WYSIWYG framework — the editor is a split-view Markdown-first surface, not a block-based rich text editor like Notion or ProseMirror.
- Not a giant editor stack — no virtual DOM, no plugin registries, no complex state management. One import, one
<div>.
Optional heading slugs: pass heading_ids: true to add id attributes for in-page anchor links. Raw HTML/SVG can be rendered via fence plugins with XSS protection — see the API docs or try the built-in html fence in quikdown_edit.js.
License
BSD 2-Clause - see LICENSE.txt
Acknowledgments
- Inspired by the simplicity of early markdown parsers
- Built for the QuikChat project
- CommonMark spec for markdown standardization
Support
- 📖 Documentation
- 🐛 Issues
- 📦 Examples hub · examples/ (source)