from multimark import markdown_to_html
print(markdown_to_html("# Welcome\n\nThis is **bold** and *italic* text.\n"))<h1>Welcome</h1>
<p>This is <strong>bold</strong> and <em>italic</em> text.</p>
This page walks you through the essentials of converting Markdown to HTML with multimark, covering inline formatting, block elements, smart punctuation, and safe HTML handling.
The most common task is converting Markdown to HTML. Import the renderer and pass it a string.
from multimark import markdown_to_html
print(markdown_to_html("# Welcome\n\nThis is **bold** and *italic* text.\n"))<h1>Welcome</h1>
<p>This is <strong>bold</strong> and <em>italic</em> text.</p>
print() vs. bare expressions
Throughout this guide, examples use print() to display rendered output. This shows the string with newlines expanded, making the HTML structure easy to read. Without print(), Python displays the repr() of the string (with escaped \n characters and surrounding quotes), which is harder to scan visually.
The output is a complete HTML fragment with proper block-level structure. Headings become <h1> through <h6> elements, paragraphs are wrapped in <p> tags, and inline formatting maps to <strong> and <em>.
Markdown supports a rich set of inline constructs. Multimark handles all of them correctly, including nested formatting.
text = """
This has **bold**, *italic*, `inline code`, and ***bold italic***.
Here is a [link](https://example.com) and an .
"""
print(markdown_to_html(text))<p>This has <strong>bold</strong>, <em>italic</em>, <code>inline code</code>, and <em><strong>bold italic</strong></em>.</p>
<p>Here is a <a href="https://example.com">link</a> and an <img src="logo.png" alt="image" title="alt text" />.</p>
Notice that the raw HTML output includes proper attribute encoding and escaping. Special characters like <, >, and & in your Markdown text are escaped automatically in the HTML output.
Beyond paragraphs and headings, Markdown supports block quotes, code blocks, and lists. Each renders to its corresponding HTML structure.
fence = "```"
blocks = (
"> A block quote with **emphasis**.\n\n"
"- Item one\n"
"- Item two\n"
"- Item three\n\n"
f"{fence}python\n"
"def hello():\n"
' print("Hello!")\n'
f"{fence}\n"
)
print(markdown_to_html(blocks))<blockquote>
<p>A block quote with <strong>emphasis</strong>.</p>
</blockquote>
<ul>
<li>Item one</li>
<li>Item two</li>
<li>Item three</li>
</ul>
<pre><code class="language-python">def hello():
print("Hello!")
</code></pre>
Fenced code blocks preserve their language annotation in a class attribute on the <code> element, which is useful for client-side syntax highlighting libraries.
Straight quotes and ASCII dashes look fine in source code but feel out of place in typeset prose. The smart option converts them to their typographic equivalents.
prose = '"It\'s a beautiful day," she said -- "let\'s go outside..."'
print(markdown_to_html(prose, smart=True))<p>“It’s a beautiful day,” she said – “let’s go outside…”</p>
With smart=True, double quotes become curly quotes, single quotes become apostrophes, -- becomes an en-dash, and ... becomes an ellipsis character. This produces publication-quality output from plain ASCII input.
By default, multimark strips raw HTML from the input for security. This is the safe choice when processing untrusted content (user comments, forum posts, uploaded files).
<!-- raw HTML omitted -->
When you control the input and need HTML passthrough, set unsafe=True.
<div class='custom'>Hello</div>
The unsafe flag affects both block-level HTML (like <div>) and inline HTML (like <span>). It also allows potentially dangerous URL schemes like javascript: in links. Only enable this for content you fully trust.
This page covered the basics of HTML rendering with multimark. The following pages explore GFM extensions, the other output formats, and the options system in depth. Each builds on what you have seen here, using the same straightforward API pattern: pass text in, get rendered output back.