GFM Extensions

This page explains the five GitHub Flavored Markdown extensions that multimark supports, with examples showing how each one works and how to combine them.

What Are GFM Extensions?

GitHub Flavored Markdown (GFM) extends the CommonMark specification with five additional syntax features. These are the constructs that GitHub recognizes in README files, issues, and pull requests but that are not part of the base CommonMark spec.

Multimark supports all five GFM extensions through the extensions parameter, which accepts a sequence of extension names. Extensions are disabled by default so that the parser remains strictly CommonMark-compliant unless you opt in.

from multimark import VALID_EXTENSIONS

sorted(VALID_EXTENSIONS)
['autolink', 'strikethrough', 'table', 'tagfilter', 'tasklist']

Each extension is independent: you enable only the ones you need.

Tables

The table extension adds pipe-table syntax. Columns are separated by | characters, and a header row is separated from body rows by a line of dashes.

from multimark import markdown_to_html

table_md = """\
| Feature    | Status |
|------------|--------|
| Tables     | ✓      |
| Autolinks  | ✓      |
| Tasklists  | ✓      |
"""

print(markdown_to_html(table_md, extensions=["table"]))
<table>
<thead>
<tr>
<th>Feature</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tables</td>
<td>✓</td>
</tr>
<tr>
<td>Autolinks</td>
<td>✓</td>
</tr>
<tr>
<td>Tasklists</td>
<td>✓</td>
</tr>
</tbody>
</table>

Column alignment is controlled with colons in the separator row: :--- for left, :---: for center, and ---: for right. The renderer emits style="text-align: ..." attributes on the <th> and <td> elements.

aligned = """\
| Left | Center | Right |
|:-----|:------:|------:|
| a    | b      | c     |
"""

print(markdown_to_html(aligned, extensions=["table"]))
<table>
<thead>
<tr>
<th align="left">Left</th>
<th align="center">Center</th>
<th align="right">Right</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">a</td>
<td align="center">b</td>
<td align="right">c</td>
</tr>
</tbody>
</table>

Strikethrough

The strikethrough extension recognizes ~~text~~ as deleted content, rendering it with <del> tags.

print(markdown_to_html("This is ~~no longer~~ relevant.", extensions=["strikethrough"]))
<p>This is <del>no longer</del> relevant.</p>

Strikethrough works inline alongside other formatting. You can combine it with bold, italic, and code spans without issue.

combined = "~~**bold and struck**~~ and ~~`code struck`~~"
print(markdown_to_html(combined, extensions=["strikethrough"]))
<p><del><strong>bold and struck</strong></del> and <del><code>code struck</code></del></p>

Tag Filtering

The tagfilter extension suppresses a specific set of HTML tags that could be used for XSS attacks, even when unsafe=True is set. The filtered tags are: <textarea>, <style>, <xmp>, <iframe>, <noembed>, <noframes>, <script>, and <plaintext>.

html_input = "Before <script>alert('xss')</script> after"
print(markdown_to_html(html_input, unsafe=True, extensions=["tagfilter"]))
<p>Before &lt;script>alert('xss')&lt;/script> after</p>

The tags are not removed entirely. Instead, the opening < is replaced with &lt;, rendering them inert while preserving the text content for visibility. This matches GitHub’s behavior for user-generated content.

Task Lists

The tasklist extension renders list items that begin with [ ] or [x] as checkbox items with a special CSS class.

tasks = """\
- [x] Write documentation
- [x] Add tests
- [ ] Publish release
"""

print(markdown_to_html(tasks, extensions=["tasklist"]))
<ul>
<li><input type="checkbox" checked="" disabled="" /> Write documentation</li>
<li><input type="checkbox" checked="" disabled="" /> Add tests</li>
<li><input type="checkbox" disabled="" /> Publish release</li>
</ul>

The checkboxes are rendered as <input> elements with type="checkbox" and a disabled attribute (since they are static content, not interactive form elements).

Combining Extensions

Extensions compose freely. Enable as many as you need by passing multiple names in the extensions list.

doc = """\
## Release Notes

| Change        | Status       |
|---------------|--------------|
| ~~Old API~~   | Removed      |
| New API       | Added        |

Tasks remaining:

- [x] Update docs at https://example.com/docs
- [ ] Tag release

Contact: releases@example.com
"""

print(markdown_to_html(doc, extensions=["table", "strikethrough", "tasklist", "autolink"]))
<h2>Release Notes</h2>
<table>
<thead>
<tr>
<th>Change</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td><del>Old API</del></td>
<td>Removed</td>
</tr>
<tr>
<td>New API</td>
<td>Added</td>
</tr>
</tbody>
</table>
<p>Tasks remaining:</p>
<ul>
<li><input type="checkbox" checked="" disabled="" /> Update docs at <a href="https://example.com/docs">https://example.com/docs</a></li>
<li><input type="checkbox" disabled="" /> Tag release</li>
</ul>
<p>Contact: <a href="mailto:releases@example.com">releases@example.com</a></p>

The order of extension names in the list does not matter. The parser enables all requested extensions before processing the input.

Extensions with Other Renderers

GFM extensions work with all five output formats, not just HTML. Here is the same table rendered as LaTeX.

from multimark import markdown_to_latex

print(markdown_to_latex(table_md, extensions=["table"]))
\begin{table}
\begin{tabular}{ll}
Feature & Status \\
Tables & ✓ \\
Autolinks & ✓ \\
Tasklists & ✓ \\
\end{tabular}
\end{table}

The LaTeX renderer maps table structure to tabular environments. Each renderer translates extension syntax into its native equivalent where possible.