quikdown

CI
npm version
Coverage Status
License: BSD-2-Clause

Bundle Sizes

quikdown
quikdown_bd
quikdown_edit

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

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

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)
![Alt text](image.jpg)

// 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

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:

const unsafe = '<script>alert("XSS")</script> **bold**';
const safe = quikdown(unsafe);
// Output: &lt;script&gt;alert("XSS")&lt;/script&gt; <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

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

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:

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:

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:

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

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:

Benchmarks show quikdown performs comparably to larger markdown parsers while maintaining a much smaller footprint.

Limitations

quikdown intentionally doesn't support:

These omissions keep the parser small, fast, and secure.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. 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

Choose quikdown when you need:

Support