pymdown-extensions
PyMdown Extensions is a large extension pack for Python-Markdown. It adds richer fenced code, highlighting, inline code highlighting, math delimiters, task lists, tabbed content, details, emoji, snippets, keys, marks, critic markup, custom block containers, and many smaller syntax features. You enable only the named extensions you need and pass each its own options. It produces HTML structure and classes; a theme, CSS, JavaScript, MathJax, KaTeX, or a client highlighter may still be required to make that output look or behave as intended.
The best extension toolbox for a Python-Markdown or MkDocs site that genuinely uses its richer syntax. It is not a portable Markdown standard and not a complete frontend, so enable a small set and own the generated HTML and assets.
Use it if
- Your documentation pipeline already uses Python-Markdown or MkDocs and needs the syntax commonly seen in Material for MkDocs sites
- You want nested fenced blocks, line highlighting, tabs, task lists, snippets, and math without maintaining separate extensions
- You need one Highlight configuration shared by fenced, indented, and inline code
- You control the Markdown authoring conventions and can keep extension configuration under version control
- Your renderer is markdown-it, Mistune, CommonMark, GitHub, or a JavaScript pipeline: these are specifically Python-Markdown extensions and their syntax is not portable by package name alone
- You need Python 3.9 or older: version 11 explicitly dropped Python 3.9 and requires Python 3.10 plus Python-Markdown 3.6 or newer
- You expect installation alone to finish the UI: Highlight can need Pygments or browser highlighting, Arithmatex needs MathJax or KaTeX, and task lists, tabs, details, and other features need matching CSS or JavaScript
- You include snippets from untrusted paths or URLs without a threat model: recent changelog entries tightened base-path traversal protection, and remote includes add network and supply-chain behavior to document builds
- You only need ordinary tables, fenced code, and a few typographic additions: the smaller built-in Python-Markdown extensions are easier to configure and less likely to change rendered HTML across a major upgrade
Setup reality
Install pymdown-extensions, but remember that Python-Markdown is the parser and PyMdown is a set of opt-in plugins. Version 11.0.1 requires Python 3.10+, Python-Markdown 3.6+, and PyYAML; install the extra when server-side Pygments highlighting is wanted. Configure extension names under the pymdownx namespace and put their settings in extension_configs with the same exact key. Ordering and combinations matter: SuperFences handles nested and custom fences, Highlight centralizes code rendering, and InlineHilite can reuse that configuration. The output is HTML, not a finished design system. Pygments output needs a stylesheet; disabling Pygments prepares language classes for a browser highlighter; Arithmatex only wraps math and requires MathJax or KaTeX scripts; tabs, task lists, details, keys, and progress bars need theme CSS and sometimes JavaScript. Snippets is build-time file inclusion, so set base_path, keep restrict_base_path enabled, decide whether missing paths should fail via check_paths, and avoid URL includes when deterministic offline builds matter. The 11.0 changelog contains a breaking B64 path restriction and drops Python 3.9; earlier majors also tightened Snippets paths. Rendered HTML can therefore change even when Markdown source does not. Pin a major, snapshot representative output, sanitize raw HTML separately if authors are untrusted, and do not enable every extension just because the package ships it. Start with the syntax your content actually uses, then add its frontend assets and test the site in both light and dark themes.
Patterns
Render Markdown with a focused extension setenable-extension-set
import markdown
html = markdown.markdown(
source,
extensions=[
'pymdownx.superfences',
'pymdownx.highlight',
'pymdownx.tasklist',
],
)Install Python-Markdown as the parser; PyMdown entries are plugins passed through its extensions argument.
Configure fenced code highlighting onceconfigure-highlighting
md = markdown.Markdown(
extensions=['pymdownx.superfences', 'pymdownx.highlight'],
extension_configs={
'pymdownx.highlight': {
'use_pygments': True,
'linenums': None,
'anchor_linenums': True,
}
},
)
html = md.convert(source)Pygments-rendered markup still needs a matching CSS stylesheet; linenums=None permits per-block line numbers.
Emit language classes for a browser highlighterprepare-client-highlighting
html = markdown.markdown(
source,
extensions=['pymdownx.superfences', 'pymdownx.highlight'],
extension_configs={
'pymdownx.highlight': {'use_pygments': False}
},
)This only prepares code tags and classes; load and secure the chosen JavaScript highlighter separately.
Render interactive-looking task list markuprender-task-lists
source = '- [x] shipped\n- [ ] document it'
html = markdown.markdown(
source,
extensions=['pymdownx.tasklist'],
extension_configs={'pymdownx.tasklist': {'custom_checkbox': True}},
)The checkboxes are presentation markup, not persisted form controls; theme CSS supplies the expected appearance.
Restrict snippet includes to a documentation directoryinclude-local-snippets
html = markdown.markdown(
source,
extensions=['pymdownx.snippets'],
extension_configs={
'pymdownx.snippets': {
'base_path': ['docs/snippets'],
'restrict_base_path': True,
'check_paths': True,
}
},
)Recent releases fixed traversal around the base path; keep the restriction on and fail builds when expected snippets are missing.
Prepare math for MathJax or KaTeXwrap-math
html = markdown.markdown(
'Inline $x^2$ and block: \n\n$$y = mx + b$$',
extensions=['pymdownx.arithmatex'],
extension_configs={'pymdownx.arithmatex': {'generic': True}},
)Arithmatex wraps the expressions but does not typeset them; the page must load and configure MathJax or KaTeX.
Enable language-aware inline codeadd-inline-highlighting
html = markdown.markdown(
'Use `#!python print("hi")` here.',
extensions=['pymdownx.inlinehilite', 'pymdownx.highlight'],
)InlineHilite delegates highlighting settings to Highlight, so configure both under their exact extension keys.
Register a custom fenced block formattercreate-custom-fence
from pymdownx.superfences import fence_div_format
config = {
'pymdownx.superfences': {
'custom_fences': [
{'name': 'diagram', 'class': 'diagram', 'format': fence_div_format}
]
}
}
html = markdown.markdown(source, extensions=['pymdownx.superfences'], extension_configs=config)A custom fence defines server-side HTML shape; any diagram rendering script and content security policy remain application work.
Reset a reusable parser between documentsreuse-markdown-instance
md = markdown.Markdown(extensions=['pymdownx.extra'])
for source in documents:
html = md.reset().convert(source)
publish(html)Python-Markdown extensions can retain per-document state; reset before converting the next independent document.
Enable extensions in mkdocs.ymlconfigure-in-mkdocs
markdown_extensions:
- pymdownx.superfences
- pymdownx.highlight:
anchor_linenums: true
linenums: null
- pymdownx.tasklist:
custom_checkbox: trueThe theme must support the generated classes; a configuration that renders in Material for MkDocs may look unfinished in another theme.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| markdown | PyPI | Python-Markdown's built-in extra, tables, fenced_code, toc, and codehilite extensions already cover the site |
| markdown-it-py | PyPI | You want a CommonMark-oriented token parser with the markdown-it plugin model |
| mistune | PyPI | You want a fast Python Markdown parser with a smaller plugin surface and direct renderer control |