A fast and complete Python implementation of Markdown with extensive extras support
—
Extensions for enhanced document structure including tables, footnotes, headers, table of contents generation, and HTML customization that improve document organization and navigation.
Comprehensive table support with multiple syntax styles and formatting options.
# tables extra - GitHub-flavored and PHP-Markdown Extra style tables
extras = ["tables"]
# wiki-tables extra - MediaWiki-style table syntax
extras = ["wiki-tables"]
# html-classes extra - add CSS classes to HTML elements
extras = {
"html-classes": {
"table": "table table-striped",
"thead": "table-header",
"code": "highlight"
}
}Usage Examples:
import markdown2
# GitHub-style tables
table_text = '''
| Name | Age | City |
|------|-----|------|
| John | 25 | NYC |
| Jane | 30 | LA |
| Bob | 35 | Chicago |
'''
html = markdown2.markdown(table_text, extras=["tables"])
# With custom CSS classes
html = markdown2.markdown(
table_text,
extras={
"tables": None,
"html-classes": {
"table": "table table-bordered table-striped",
"thead": "table-dark"
}
}
)
# Wiki-style tables
wiki_table = '''
{| class="wikitable"
|-
! Name !! Age !! City
|-
| John || 25 || NYC
|-
| Jane || 30 || LA
|}
'''
html = markdown2.markdown(wiki_table, extras=["wiki-tables"])Comprehensive footnote support with customizable formatting and back-references.
# footnotes extra - support footnotes with back-references
extras = ["footnotes"]
# With custom configuration
extras = ["footnotes"]
footnote_title = "Jump back to footnote"
footnote_return_symbol = "↩"Usage Examples:
import markdown2
footnote_text = '''
This is a statement with a footnote[^1].
Here's another statement[^note].
[^1]: This is the first footnote.
[^note]: This is a named footnote with more detail.
It can span multiple paragraphs.
Like this one.
'''
html = markdown2.markdown(
footnote_text,
extras=["footnotes"],
footnote_title="Return to text",
footnote_return_symbol="⤴"
)Add IDs and navigation anchors to headers for document navigation.
# header-ids extra - add "id" attributes to headers
extras = ["header-ids"]
# tag-friendly extra - require space between # and header text
extras = ["tag-friendly"]
# With configuration options
extras = {
"header-ids": {
"prefix": "section-", # Prefix for header IDs
"mixed": False, # Use mixed case (default: False)
"reset-count": True # Reset counter for each document
}
}Usage Examples:
import markdown2
header_text = '''
# Introduction
This is the introduction section.
## Getting Started
How to get started with the project.
### Installation
Step-by-step installation guide.
## Configuration
Configuration options and examples.
'''
html = markdown2.markdown(header_text, extras=["header-ids"])
# Generates: <h1 id="introduction">Introduction</h1>
# With custom prefix
html = markdown2.markdown(
header_text,
extras={"header-ids": {"prefix": "doc-"}}
)
# Generates: <h1 id="doc-introduction">Introduction</h1>
# Tag-friendly mode (requires space after #)
tag_text = '''
# Valid Header (space after #)
#Invalid Header (no space - won't be processed as header)
'''
html = markdown2.markdown(tag_text, extras=["tag-friendly"])Automatic table of contents generation with customizable depth and formatting.
# toc extra - generate table of contents (implies header-ids)
extras = ["toc"]
# With depth configuration
extras = {
"toc": {
"depth": 3 # Include headers up to h3 level
}
}Usage Examples:
import markdown2
document_text = '''
# Chapter 1: Introduction
Content for chapter 1.
## Section 1.1: Overview
Overview content.
### Subsection 1.1.1: Details
Detailed content.
## Section 1.2: Getting Started
Getting started content.
# Chapter 2: Advanced Topics
Advanced content.
'''
html = markdown2.markdown(document_text, extras=["toc"])
# Access the generated table of contents
if hasattr(html, 'toc_html') and html.toc_html:
print("Table of Contents HTML:")
print(html.toc_html)
# With custom depth
html = markdown2.markdown(
document_text,
extras={"toc": {"depth": 2}} # Only h1 and h2
)Extract and process document metadata from YAML front matter.
# metadata extra - extract YAML-style metadata from document header
extras = ["metadata"]Usage Examples:
import markdown2
document_with_metadata = '''---
title: My Document
author: John Doe
date: 2023-01-15
tags: [markdown, documentation, tutorial]
draft: false
---
# Document Content
This is the actual document content that follows the metadata.
'''
html = markdown2.markdown(document_with_metadata, extras=["metadata"])
# Access extracted metadata
if hasattr(html, 'metadata') and html.metadata:
print(f"Title: {html.metadata['title']}")
print(f"Author: {html.metadata['author']}")
print(f"Date: {html.metadata['date']}")
print(f"Tags: {html.metadata['tags']}")
print(f"Draft: {html.metadata['draft']}")Process markdown within HTML blocks and add custom CSS classes.
# markdown-in-html extra - process markdown inside HTML blocks
extras = ["markdown-in-html"]
# html-classes extra - add CSS classes to generated HTML elements
extras = {
"html-classes": {
"img": "img-responsive",
"table": "table table-striped",
"pre": "prettyprint",
"code": "highlight",
"ul": "list-unstyled",
"ol": "numbered-list"
}
}Usage Examples:
import markdown2
# Markdown inside HTML
html_with_markdown = '''
<div class="content">
<markdown="1">
## This is a header inside HTML
This **bold text** and *italic text* will be processed.
- List item 1
- List item 2
</markdown>
</div>
'''
html = markdown2.markdown(html_with_markdown, extras=["markdown-in-html"])
# Adding CSS classes to elements
content = '''

| Col 1 | Col 2 |
|-------|-------|
| Data | More |
```python
code here'''
html = markdown2.markdown( content, extras={ "tables": None, "fenced-code-blocks": None, "html-classes": { "img": "img-fluid rounded", "table": "table table-hover", "pre": "bg-light p-3", "code": "text-primary" } } )
## Advanced Structure Processing
### Complete Document Processing
Combine multiple structure extras for comprehensive document processing:
```python
import markdown2
# Academic paper or technical documentation setup
academic_processor = markdown2.Markdown(
extras={
"metadata": None, # YAML front matter
"toc": {"depth": 4}, # Deep table of contents
"header-ids": { # Prefixed header IDs
"prefix": "sec-",
"reset-count": True
},
"footnotes": None, # Academic citations
"tables": None, # Data tables
"fenced-code-blocks": None, # Code examples
"html-classes": { # Bootstrap styling
"table": "table table-striped table-bordered",
"img": "img-fluid figure-img",
"pre": "bg-light p-3 rounded"
}
},
footnote_title="Return to reference",
footnote_return_symbol="↩"
)
# Process academic document
html = academic_processor.convert(academic_document)
# Access all generated content
print("Main content length:", len(html))
if html.metadata:
print("Document metadata:", html.metadata)
if html.toc_html:
print("Table of contents available")Structure extras for content management systems:
import markdown2
# Blog post processor
blog_processor = markdown2.Markdown(
extras={
"metadata": None, # Post metadata (title, date, tags)
"header-ids": None, # Anchor links
"tables": None, # Content tables
"footnotes": None, # References
"break-on-newline": None, # GitHub-style breaks
"smarty-pants": None, # Typography
"html-classes": { # Custom styling
"table": "post-table",
"img": "post-image",
"pre": "post-code"
}
}
)
blog_html = blog_processor.convert(blog_post_markdown)
# Extract metadata for CMS
if blog_html.metadata:
post_title = blog_html.metadata.get('title', 'Untitled')
post_tags = blog_html.metadata.get('tags', [])
post_date = blog_html.metadata.get('date')GitHub-style task lists with checkboxes for todo items and interactive elements.
# task_list extra - GitHub-style task lists
extras = ["task_list"]Usage Examples:
import markdown2
task_content = '''
## Todo List
- [x] Completed task
- [ ] Incomplete task
- [x] Another completed item
- [ ] Still need to do this
## Project Status
- [x] Research phase complete
- [x] Design approved
- [ ] Implementation in progress
- [ ] Testing pending
- [ ] Documentation needed
'''
html = markdown2.markdown(task_content, extras=["task_list"])
# Converts to HTML with checkbox inputsSupport for XML processing instructions and namespaced XML tags.
# xml extra - XML processing support
extras = ["xml"]Adjust header levels by demoting them by a specified number of levels.
# demote-headers extra - demote header levels
extras = {"demote-headers": 1} # Demote by 1 level (H1 becomes H2, etc.)Usage Examples:
import markdown2
# Original headers: H1, H2, H3
content = '''
# Main Title
## Section Title
### Subsection
'''
# Demote all headers by 2 levels
html = markdown2.markdown(content, extras={"demote-headers": 2})
# Results in: H3, H4, H5header_config = {
"prefix": "", # Prefix for generated IDs
"mixed": False, # Preserve mixed case in IDs
"reset-count": True # Reset counter for each conversion
}toc_config = {
"depth": 6 # Maximum header level to include (1-6)
}html_classes_config = {
"img": "css-class-for-images",
"table": "css-class-for-tables",
"thead": "css-class-for-table-headers",
"pre": "css-class-for-code-blocks",
"code": "css-class-for-inline-code",
"ul": "css-class-for-unordered-lists",
"ol": "css-class-for-ordered-lists"
}import markdown2
# Flask/Django integration example
def render_markdown_post(markdown_content):
processor = markdown2.Markdown(
extras={
"metadata": None,
"toc": {"depth": 3},
"header-ids": None,
"tables": None,
"footnotes": None,
"html-classes": {
"table": "table table-striped",
"img": "img-responsive"
}
}
)
html = processor.convert(markdown_content)
return {
'content': str(html),
'metadata': getattr(html, 'metadata', {}),
'toc': getattr(html, 'toc_html', None)
}Install with Tessl CLI
npx tessl i tessl/pypi-markdown2