X Xerobit

Markdown to HTML Converter — Convert Markdown Files to HTML

Converting Markdown to HTML transforms markup syntax to web-ready HTML tags. Here's how to convert Markdown to HTML in JavaScript and Python, customize the output, and handle...

Mian Ali Khalid · · 5 min read
Use the tool
Markdown Preview
Live Markdown preview with GitHub-flavored syntax. Tables, task lists, code blocks, strikethrough. Side-by-side editor and rendered output.
Open Markdown Preview →

Converting Markdown to HTML means parsing Markdown syntax and generating the corresponding HTML tags. Every # becomes <h1>, every **text** becomes <strong>text</strong>, and code blocks become <pre><code> elements. The right library handles edge cases, security, and extensions automatically.

Use the Markdown Preview to write Markdown and see the HTML output in real time.

The conversion mapping

MarkdownHTML output
# Heading<h1>Heading</h1>
**bold**<strong>bold</strong>
*italic*<em>italic</em>
`code`<code>code</code>
[text](url)<a href="url">text</a>
![alt](src)<img alt="alt" src="src">
> quote<blockquote><p>quote</p></blockquote>
---<hr>
- item<ul><li>item</li></ul>
1. item<ol><li>item</li></ol>

JavaScript: marked

marked is the most popular JavaScript Markdown parser:

npm install marked
import { marked } from 'marked';

const markdown = `
# Hello World

This is **bold** and *italic* text.

\`\`\`javascript
const greeting = 'Hello';
console.log(greeting);
\`\`\`

- Item one
- Item two
`;

// Basic conversion:
const html = marked(markdown);
console.log(html);

// With options:
import { marked, Renderer } from 'marked';
import hljs from 'highlight.js';

marked.setOptions({
  breaks: true,          // Convert single newlines to <br>
  gfm: true,             // GitHub Flavored Markdown
  headerIds: true,       // Add id attributes to headings
  mangle: false,         // Don't mangle header IDs
});

// Custom renderer for code blocks with syntax highlighting:
const renderer = new Renderer();
renderer.code = (code, language) => {
  const highlighted = language && hljs.getLanguage(language)
    ? hljs.highlight(code, { language }).value
    : hljs.highlightAuto(code).value;
  
  return `<pre><code class="hljs language-${language}">${highlighted}</code></pre>`;
};

marked.use({ renderer });

const html = marked(markdown);

JavaScript: markdown-it

markdown-it is more extensible and CommonMark-compliant:

npm install markdown-it
import MarkdownIt from 'markdown-it';

const md = new MarkdownIt({
  html: false,        // Disable raw HTML in Markdown (security)
  linkify: true,      // Auto-convert URLs to links
  typographer: true,  // Smart quotes, dashes, etc.
});

// With plugins:
import markdownItAnchor from 'markdown-it-anchor';
import markdownItToc from 'markdown-it-toc-done-right';

md.use(markdownItAnchor, { permalink: true, permalinkSymbol: '#' });
md.use(markdownItToc, { containerClass: 'table-of-contents' });

const html = md.render(markdown);

markdown-it plugins

// Syntax highlighting (with shiki):
import Shiki from 'markdown-it-shiki';
md.use(Shiki, { theme: 'github-dark' });

// Math rendering (KaTeX):
import markdownItKatex from '@traptitech/markdown-it-katex';
md.use(markdownItKatex);

// Footnotes:
import markdownItFootnote from 'markdown-it-footnote';
md.use(markdownItFootnote);

// Container blocks:
import markdownItContainer from 'markdown-it-container';
md.use(markdownItContainer, 'warning', {
  render: (tokens, idx) => {
    return tokens[idx].nesting === 1 
      ? '<div class="warning">' 
      : '</div>';
  }
});

Python: markdown library

pip install markdown
import markdown

text = """
# Hello World

This is **bold** and *italic* text.

```python
def hello():
    print("Hello!")
  • Item one
  • Item two """

Basic conversion:

html = markdown.markdown(text)

With extensions:

html = markdown.markdown(text, extensions=[ ‘fenced_code’, # Fenced code blocks (```) ‘codehilite’, # Syntax highlighting (requires Pygments) ‘tables’, # GFM-style tables ‘toc’, # Table of contents ‘nl2br’, # Newlines to
‘attr_list’, # Add HTML attributes to elements ])

With config:

html = markdown.markdown(text, extensions=[ markdown.extensions.codehilite.CodeHiliteExtension( guess_lang=False, css_class=‘highlight’ ), ‘toc’, ], extension_configs={ ‘toc’: { ‘title’: ‘Table of Contents’, ‘permalink’: True, } })


## Python: mistune (fast alternative)

```bash
pip install mistune
import mistune

# Simple usage:
html = mistune.html(markdown_text)

# With plugins and options:
from mistune.plugins import plugin_table, plugin_task_lists

md = mistune.create_markdown(
    plugins=[plugin_table, plugin_task_lists],
    renderer=mistune.HTMLRenderer(escape=True)  # Sanitize HTML
)

html = md(markdown_text)

Security: sanitizing HTML output

Never render untrusted Markdown directly to HTML without sanitization. A malicious user could inject:

  • <script> tags via raw HTML in Markdown
  • Event handlers via HTML attributes
  • Links to javascript: URLs
import { marked } from 'marked';
import DOMPurify from 'dompurify';

function renderMarkdown(markdown) {
  const rawHtml = marked(markdown);
  // Sanitize: remove <script>, onclick, javascript: links, etc.
  return DOMPurify.sanitize(rawHtml);
}

// Or configure marked to strip HTML:
marked.setOptions({ sanitize: true }); // DEPRECATED in marked v5
// Better: use a separate sanitizer

Server-side sanitization (Node.js):

import sanitizeHtml from 'sanitize-html';

const clean = sanitizeHtml(rawHtml, {
  allowedTags: ['h1', 'h2', 'h3', 'p', 'a', 'strong', 'em', 'ul', 'li', 'ol', 
                'code', 'pre', 'blockquote', 'img', 'br', 'hr', 'table', 'tr', 'td', 'th'],
  allowedAttributes: {
    a: ['href', 'title', 'target'],
    img: ['src', 'alt', 'title'],
    code: ['class'],
    pre: ['class'],
  }
});

Server-side vs client-side rendering

Server-side (recommended for SEO): Convert Markdown in your build process or server handler, send HTML to the client. The page is crawlable and loads without JavaScript.

Client-side: Parse Markdown in the browser. Useful for editors, live preview, and user-generated content. Requires the JavaScript bundle for the parser.

// Client-side (browser):
import { marked } from 'marked';

const textarea = document.getElementById('markdown-input');
const preview = document.getElementById('html-output');

textarea.addEventListener('input', () => {
  preview.innerHTML = DOMPurify.sanitize(marked(textarea.value));
});

Related posts

Related tool

Markdown Preview

Live Markdown preview with GitHub-flavored syntax. Tables, task lists, code blocks, strikethrough. Side-by-side editor and rendered output.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.