quikdown
Bundle Sizes
A lightweight, fast markdown parser with built-in XSS protection and optional bidirectional conversion support. Quikdown works in both browser and Node.js environments. Via its fenced plug-in support it can support highlighted code blocks, diagrams, and other custom fenced content.
A separate quikdown-editor based on the quikdown parser is also provided as a simple drop-in editor control that should work in any div.
🚀 Try Live Demo - Interactive markdown to HTML parser with real-time preview
🚀 Bidirectional Parser Demo - Bidirectional parser demo with custom editor (edit markdown or HTML and see updates)
🚀 Quikdown Editor Demo - Standalone drop-in editor control with source/split/preview modes
📚 View Examples - Additional demos and test pages
📖 Read Documentation - Architecture, security, API reference, and plugin guide
Table of Contents
- Features
- Installation
- Quick Start
- Bidirectional Conversion
- Quikdown Editor
- Supported Markdown
- Configuration Options
- Plugin System
- Framework Integration
- API Reference
- Security
- Contributing
Features
- 📦 Zero dependencies - No external libraries required
- 🌐 Universal - Works in browsers and Node.js
- 🚀 Lightweight - 8.5KB minified (quikdown), 12.5KB (quikdown_bd), 24.3KB (quikdown_edit)
- 🔒 Secure by default - Built-in XSS protection with URL sanitization
- 🎨 Flexible styling - Inline styles or CSS classes including light and dark mode generation, custom themes
- 🔌 Plugin system - Extensible fence block handlers
- ⚡ Fast - Optimized regex-based parsing
- 📝 CommonMark subset - Supports essential markdown features
- ✅ Task Lists - GitHub-style checkboxes
- 🔗 Autolinks - Automatic URL detection
- 🔄 Bidirectional - Convert HTML back to Markdown (requires
quikdown_bd
module) - 💬 Lazy Linefeeds - Single newlines become line breaks (for chat/LLM output), api controllable
Installation
npm install quikdown
Or via CDN:
ES Modules (recommended for modern applications):
<script type="module">
import quikdown from 'https://unpkg.com/quikdown/dist/quikdown.esm.min.js';
const html = quikdown('# Hello World');
document.body.innerHTML = html;
</script>
UMD (for legacy browser support):
<script src="https://unpkg.com/quikdown/dist/quikdown.umd.min.js"></script>
<script>
// Available as window.quikdown
const html = quikdown('# Hello World');
</script>
Production tip: Pin to a specific version for stability (e.g.,
https://unpkg.com/quikdown@1.0.4/dist/quikdown.esm.min.js
)
Complete CDN Example
See our complete CDN demo that includes QuikDown with Highlight.js and Mermaid - all loaded from CDN. Perfect for copying as a starting template!
Quick Start
Basic Usage (Standard - One-way conversion)
import quikdown from 'quikdown';
const markdown = '# Hello World\n\nThis is **bold** text.';
const html = quikdown(markdown);
console.log(html);
// Output: <h1>Hello World</h1><p>This is <strong>bold</strong> text.</p>
// Note: Regular quikdown does NOT support HTML to Markdown conversion
Bidirectional Usage (Two-way conversion)
// Use quikdown_bd for bidirectional support
import quikdown_bd from 'quikdown/bd';
const markdown = '# Hello World\n\nThis is **bold** text.';
// Markdown to HTML
const html = quikdown_bd(markdown);
// HTML back to Markdown (only available in quikdown_bd)
const recoveredMarkdown = quikdown_bd.toMarkdown(html);
console.log(recoveredMarkdown);
// Output: # Hello World\n\nThis is **bold** text.
With Options
const html = quikdown(markdown, {
inline_styles: true, // Use inline styles instead of classes
fence_plugin: myFenceHandler // Custom code fence handler
});
TypeScript Support
quikdown includes TypeScript definitions for better IDE support and type safety:
import quikdown, { QuikdownOptions } from 'quikdown';
const options: QuikdownOptions = {
inline_styles: true,
fence_plugin: (content: string, language: string) => {
return `<pre class="hljs ${language}">${content}</pre>`;
}
};
const html: string = quikdown(markdown, options);
Supported Markdown
quikdown supports a practical subset of CommonMark:
Text Formatting
- Bold:
**text**
or__text__
- Italic:
*text*
or_text_
Strikethrough:~~text~~
Code
:`code`
Headings
# H1 Heading
## H2 Heading
### H3 Heading
#### H4 Heading
##### H5 Heading
###### H6 Heading
Lists
Unordered:
- Item 1
- Item 2
- Nested item
* Also works with asterisks
Ordered:
1. First item
2. Second item
1. Nested item
Task Lists:
- [x] Completed task
- [ ] Pending task
- [ ] Another todo
Links and Images
[Link text](https://example.com)

// Autolinks - URLs are automatically linked
Visit https://github.com for more info
Code Blocks
```javascript
console.log('Hello, world!');
```
// Also supports ~~~ fences
~~~python
print("Hello, world!")
~~~
Tables
| Header 1 | Header 2 |
|----------|----------|
| Cell 1 | Cell 2 |
| Cell 3 | Cell 4 |
Other Elements
- Blockquotes:
> Quote text
- Horizontal rules:
---
or***
or___
- Line breaks: Two spaces at end of line or
<br>
Configuration Options
lazy_linefeeds
(boolean)
When true
, single newlines become <br>
tags. Perfect for chat/LLM applications.
// With lazy_linefeeds: false (default)
quikdown('Line 1\nLine 2');
// Output: <p>Line 1\nLine 2</p>
// With lazy_linefeeds: true
quikdown('Line 1\nLine 2', { lazy_linefeeds: true });
// Output: <p>Line 1<br>Line 2</p>
inline_styles
(boolean)
When true
, uses inline styles for formatting. When false
, uses CSS classes.
// With inline_styles: true
quikdown('**bold**', { inline_styles: true });
// Output: <strong style="font-weight: bold;">bold</strong>
// With inline_styles: false (default)
quikdown('**bold**', { inline_styles: false });
// Output: <strong>bold</strong>
fence_plugin
(function)
Custom handler for fenced code blocks. Useful for syntax highlighting or diagrams.
function fencePlugin(code, language) {
if (language === 'mermaid') {
return `<div class="mermaid">${code}</div>`;
}
// Return undefined to use default handling
}
const html = quikdown(markdown, { fence_plugin: fencePlugin });
Plugin System
For a complete plugin development guide, see docs/plugin-guide.md
Creating a Fence Plugin
Fence plugins allow you to customize how code blocks are rendered:
function myFencePlugin(code, language) {
// Handle specific languages
if (language === 'graph') {
return renderGraph(code);
}
// Add syntax highlighting
if (language && hljs.getLanguage(language)) {
const highlighted = hljs.highlight(code, { language }).value;
return `<pre><code class="language-${language}">${highlighted}</code></pre>`;
}
// Return undefined for default handling
return undefined;
}
Example: Mermaid Diagrams
function mermaidPlugin(code, language) {
if (language === 'mermaid') {
const id = 'mermaid-' + Math.random().toString(36).substr(2, 9);
// Render with mermaid.js after DOM update
setTimeout(() => {
mermaid.render(id + '-svg', code).then(result => {
document.getElementById(id).innerHTML = result.svg;
});
}, 0);
return `<div id="${id}" class="mermaid">Loading diagram...</div>`;
}
}
const html = quikdown(markdownWithMermaid, {
fence_plugin: mermaidPlugin
});
Security
For detailed security information, see docs/security.md
quikdown includes built-in XSS protection:
- All HTML tags in markdown are escaped by default
- Attributes are sanitized
- JavaScript URLs are blocked
- Only safe markdown constructs are converted to HTML
const unsafe = '<script>alert("XSS")</script> **bold**';
const safe = quikdown(unsafe);
// Output: <script>alert("XSS")</script> <strong>bold</strong>
Bidirectional Conversion
⚠️ Important: Full bidirectional conversion (including HTML-to-Markdown) requires the quikdown_bd
module, not the regular quikdown
module.
The quikdown_bd
module is a separate build that includes both markdown-to-HTML and HTML-to-markdown conversion capabilities. It's perfect for WYSIWYG editors and round-trip conversion scenarios.
Note: As of v1.0.5, the core quikdown
module supports the bidirectional
option to emit data-qd
attributes, but only quikdown_bd
includes the toMarkdown()
function for converting HTML back to Markdown.
Installation
// ES Modules - Use quikdown_bd, NOT quikdown
import quikdown_bd from 'quikdown/bd';
// CommonJS - Use quikdown_bd, NOT quikdown
const quikdown_bd = require('quikdown/bd');
// Browser - Load the quikdown_bd script, NOT the regular quikdown
<script src="https://unpkg.com/quikdown/dist/quikdown_bd.umd.min.js"></script>
<script>
// Available as window.quikdown_bd
const html = quikdown_bd(markdown);
</script>
Basic Usage
// IMPORTANT: Use quikdown_bd for bidirectional support
import quikdown_bd from 'quikdown/bd';
// Markdown to HTML with source tracking (bidirectional is automatic)
const html = quikdown_bd('**Hello** world');
console.log(html);
// <strong data-qd="**">Hello</strong> world
// HTML back to Markdown (only available in quikdown_bd)
const markdown = quikdown_bd.toMarkdown(html);
console.log(markdown);
// **Hello** world
// Note: Regular quikdown does NOT have toMarkdown method
// This will fail: quikdown.toMarkdown(html) // ❌ Error
Use Cases
- Live Editors: Build WYSIWYG markdown editors where users can edit in either view
- Content Migration: Convert existing HTML content to Markdown
- Round-trip Preservation: Maintain markdown source formatting through HTML conversion
- Collaborative Editing: Enable rich-text editing while storing content as Markdown
Browser Example
<div id="editor" contenteditable="true"></div>
<script type="module">
import quikdown_bd from 'https://unpkg.com/quikdown/dist/quikdown_bd.esm.min.js';
const editor = document.getElementById('editor');
const markdown = '# Edit me\n\n**Bold** and *italic*';
// Convert to HTML and display
editor.innerHTML = quikdown_bd(markdown, { bidirectional: true });
// Convert back to Markdown when needed
editor.addEventListener('blur', () => {
const updatedMarkdown = quikdown_bd.toMarkdown(editor);
console.log('Updated markdown:', updatedMarkdown);
});
</script>
For complete documentation, see Bidirectional Documentation.
Quikdown Editor
Quikdown Editor is a drop-in markdown editor control that can be embedded in any webpage. It provides a complete editing experience with live preview, bidirectional editing, and plugin support.
🚀 Try Editor Demo - Full-featured editor demonstration
Features
- 📝 Three view modes: Source, Split, and Preview
- 🔄 Bidirectional editing: Edit markdown or preview and see changes sync
- 🎨 Theme support: Light, dark, and auto themes
- 🔌 Plugin integration: Built-in support for Highlight.js and Mermaid
- ⌨️ Keyboard shortcuts: Quick mode switching with Ctrl+1/2/3
- 📋 Copy functions: Copy markdown or HTML to clipboard
- 📱 Responsive: Mobile-friendly interface
Quick Start (Just 3 Lines!)
<div id="editor" style="height: 400px;"></div>
<script type="module">
import QuikdownEditor from 'https://unpkg.com/quikdown/dist/quikdown_edit.esm.min.js';
new QuikdownEditor('#editor');
</script>
That's it! For more options:
const editor = new QuikdownEditor('#editor', {
mode: 'split', // 'source', 'split', or 'preview'
plugins: { highlightjs: true, mermaid: true } // Enable plugins
});
editor.setMarkdown('# Hello World'); // Set content
const markdown = editor.getMarkdown(); // Get content
API
// Create editor
const editor = new QuikdownEditor(container, options);
// Methods
editor.setMarkdown(markdown); // Set markdown content
editor.getMarkdown(); // Get current markdown
editor.getHTML(); // Get rendered HTML
editor.setMode('split'); // Change view mode
editor.destroy(); // Clean up editor
// Properties
editor.markdown; // Get/set markdown
editor.html; // Get HTML (read-only)
editor.mode; // Get current mode
Options
Option | Type | Default | Description |
---|---|---|---|
mode |
string | 'split' | View mode: 'source', 'split', or 'preview' |
theme |
string | 'auto' | Theme: 'light', 'dark', or 'auto' |
showToolbar |
boolean | true | Show/hide toolbar |
lazy_linefeeds |
boolean | false | Enable lazy linefeeds |
debounceDelay |
number | 300 | Debounce delay in ms |
placeholder |
string | 'Start typing...' | Placeholder text |
plugins |
object | {} | Plugin configuration |
For complete documentation, see Quikdown Editor Documentation.
Quikdown Lexer Version
An experimental lexer-based implementation is available for testing. See docs/lexer-implementation.md for details.
Framework Integration
quikdown integrates seamlessly with modern JavaScript frameworks:
- React - Hooks, components, and Next.js support
- Vue - Composition API, Options API, and Nuxt support
- Svelte - Reactive statements and stores
- Angular - Components, services, and pipes
See the Framework Integration Guide for detailed examples and best practices.
API Reference
For complete API documentation, see docs/api-reference.md
Core API
quikdown(markdown, options?)
Main function to convert markdown to HTML.
Parameters:
markdown
(string): The markdown text to convertoptions
(object, optional):inline_styles
(boolean): Use inline styles instead of classesfence_plugin
(function): Custom fence block handler
Returns: HTML string
quikdown.configure(options)
Creates a configured instance of the parser.
const myParser = quikdown.configure({
inline_styles: true,
fence_plugin: myPlugin
});
// Use the configured parser
const html = myParser(markdown);
quikdown.emitStyles(prefix?, theme?)
Returns CSS styles for quikdown HTML output when not using inline styles.
// Get light theme CSS
const lightStyles = quikdown.emitStyles();
// Get dark theme CSS
const darkStyles = quikdown.emitStyles('quikdown-', 'dark');
Bidirectional API
⚠️ These methods are only available in quikdown_bd
, not in regular quikdown
:
quikdown_bd(markdown, options?)
Converts markdown to HTML with source tracking for bidirectional conversion.
quikdown_bd.toMarkdown(htmlOrElement)
Converts HTML back to Markdown. This method only exists in quikdown_bd
.
Parameters:
htmlOrElement
(string | HTMLElement): HTML string or DOM element
Returns: Markdown string
// ✅ Correct - using quikdown_bd
import quikdown_bd from 'quikdown/bd';
const markdown = quikdown_bd.toMarkdown(html);
// ❌ Wrong - regular quikdown doesn't have toMarkdown
import quikdown from 'quikdown';
const markdown = quikdown.toMarkdown(html); // Error: toMarkdown is not a function
See API Reference for complete documentation.
Theming
QuikDown supports flexible theming through container-based CSS scoping:
Using Pre-built Themes
<!-- Load theme CSS files -->
<link rel="stylesheet" href="quikdown.light.css">
<link rel="stylesheet" href="quikdown.dark.css">
<!-- Apply themes via container classes -->
<div class="quikdown-light">
<!-- Light themed content -->
</div>
<div class="quikdown-dark">
<!-- Dark themed content -->
</div>
Theme Architecture
- Structural styles: Shared across all themes (margins, padding, font-sizes)
- Theme colors: Scoped to container classes (
.quikdown-light
,.quikdown-dark
) - No conflicts: Multiple themes can coexist on the same page
- No default theme: Without a container class, only structural styles apply
Inline Styles
For a batteries-included approach without CSS files:
// Use inline styles (always light theme currently)
const html = quikdown(markdown, { inline_styles: true });
Browser Usage
ES Modules (Recommended)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="output"></div>
<script type="module">
import quikdown from 'https://unpkg.com/quikdown/dist/quikdown.esm.min.js';
const markdown = '# Hello quikdown!\n\nSupports **bold** and *italic* text.';
const html = quikdown(markdown, { inline_styles: true });
document.getElementById('output').innerHTML = html;
</script>
</body>
</html>
UMD Script Tag (Legacy)
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/quikdown/dist/quikdown.umd.min.js"></script>
</head>
<body>
<div id="output"></div>
<script>
const markdown = '# Hello quikdown!';
const html = quikdown(markdown, { inline_styles: true });
document.getElementById('output').innerHTML = html;
</script>
</body>
</html>
Node.js Usage
const quikdown = require('quikdown');
const markdown = '# Server-side Markdown';
const html = quikdown(markdown);
// Use in Express, etc.
res.send(html);
Performance
quikdown is optimized for speed:
- Single-pass regex parsing
- Minimal memory allocation
- No AST generation
- Efficient string operations
Benchmarks show quikdown performs comparably to larger markdown parsers while maintaining a much smaller footprint.
Limitations
quikdown intentionally doesn't support:
- HTML blocks (for security)
- Reference-style links
- Footnotes
- Definition lists
- Complex table alignment
- Nested blockquotes with different markers
These omissions keep the parser small, fast, and secure.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature
) - Commit your changes (
git commit -m 'Add some AmazingFeature'
) - Push to the branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Testing
# Run tests
npm test
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverage
License
BSD 2-Clause License - see LICENSE.txt file for details.
Acknowledgments
- Inspired by the simplicity of early markdown parsers
- Built for the QuikChat project
- CommonMark spec for markdown standardization
Choose quikdown when you need:
- A lightweight solution
- Built-in security
- Simple plugin system
- Zero dependencies
Support
- 🐛 Issues: GitHub Issues