Markdown checkboxes in tables: working options

Use a status word or Unicode symbol for portable Markdown checkboxes in tables; use an app's table-task extension when people must tick and save them. Choose among three routes: plain status text, HTML controls in output you control, and host-specific tasks that write changes back. A checkbox appearing on screen does not establish that the surrounding table parsed or that the tick will survive reopening.

Choose a route for Markdown checkboxes in tables

Match the representation to whether readers need a readable status, an HTML control, or a saved task.

What you need or see How to identify the case Working route
A status report that travels between Markdown tools Readers need Done or Pending, without editing through a control Keep a status word, optionally paired with ☐ or ☑
Literal pipes, sometimes with visible checkbox controls The generated HTML contains a paragraph with pipes, without a table Enable table parsing before changing the checkbox syntax
A table whose cells still show brackets The cells exist, but their content remains [ ], [x], or - [ ] Use readable status text or an explicit table-task extension
HTML tags shown as text, or controls missing after publishing Tags are escaped in generated HTML, or controls present there disappear on the host Resolve HTML handling, or keep the status as text
Ticks that must survive reopening After a toggle, the saved source is unchanged or the tick resets on reopening Use the host's supported task mechanism

Our local comparison separated these cases by changing the table rule and HTML option while keeping the input fixed. The same HTML checkbox source could produce controls beside literal pipes, controls inside a table, or escaped tag text inside a table. 10

Keep shared status as words or symbols

Use Done and Pending in a table when the document needs to communicate status across tools.

| Task | Status |
        | --- | --- |
        | Draft release notes | ☐ Pending |
        | Review schema | ☑ Done |
        

The table-capable configurations kept the status words and symbols as cell text. A processor without table support still has readable source, although it does not produce the table layout. The symbols add a visual cue, while the words carry the meaning if a reader's display or font handles the glyph differently. These cells contain no interactive controls. 10

Output from our status-word sample, unedited:

<tr>
        <td>Draft release notes</td>
        <td>☐ Pending</td>
        </tr>
        <tr>
        <td>Review schema</td>
        <td>☑ Done</td>
        </tr>
        

That excerpt comes from Python-Markdown with its tables extension. The rows retain the task-to-state relationship without requiring task-list processing. To update the report, edit the word and its symbol together. Keeping a tick beside Pending would leave conflicting information in the source. 310

The same representation fits manual cleanup after PDF to Markdown conversion. If a source document contains a checklist table, retain each task label and the completion state while reviewing the converted content. Represent a static result with a status word, or move actionable items into a separate task list. Conversion to Markdown does not itself establish interactive table-task behavior.

Separate table parsing from task recognition

Table support and task-list processing are separate features, so enabling a table rule does not turn its cell text into tasks.

The GFM specification gives pipe-table cells inline content. Its task-list extension operates on list items. A hyphen written inside a cell remains cell content instead of starting a block list. In the installed markdown-it-py table parser, the cell becomes an inline token, without a list-item container around its marker. 112

Our hyphen-prefixed table sample kept the brackets when we enabled markdown-it-py's table rule. 210

Output from our hyphen-prefixed table sample, unedited:

<tr>
        <td>- [ ] Draft release notes</td>
        <td>Writer</td>
        </tr>
        <tr>
        <td>- [x] Review schema</td>
        <td>Reviewer</td>
        </tr>
        

Adding <br> before a task-looking marker changed the line break in configurations that retained HTML, but left the following brackets as text. A break inside a cell did not create the list-item structure. Our Python-Markdown configuration enabled tables and fenced code, with no task-list extension. It retained the markers inside the table and in our ordinary list control. 1012

Pandoc with its GFM reader did recognize the standalone task list. The corresponding table markers still remained text. Moving actionable work out of a table therefore has a concrete effect when the selected renderer supports task lists. Keep the same task labels beside the table so readers can associate the checklist with its rows. 410

Output from our standalone task-list sample, unedited:

<ul class="task-list">
        <li><label><input type="checkbox" />Draft release notes</label></li>
        <li><label><input type="checkbox" checked="" />Review
        schema</label></li>
        </ul>
        

HTML introduces a different failure shape: controls can appear even when table parsing is absent. Our CommonMark configuration passed raw input elements through inside a paragraph containing the pipe characters. The output had checkboxes and no table. 10

Output from our HTML-checkbox sample under CommonMark, unedited:

<p>| Task | Status |
        | --- | --- |
        | Draft release notes | <input type="checkbox" disabled aria-label="Draft release notes"> |
        | Review schema | <input type="checkbox" disabled checked aria-label="Review schema"> |</p>
        
Failure condition Observable output Appropriate change
Table rule absent Pipes remain inside a paragraph, even if raw controls survive Enable the parser's table feature
Task-like text inside an inline cell A table exists, with brackets still in its cells Choose a status representation or host table-task support
Ordinary list retains brackets too List items exist, but the task marker is unchanged Select task-list processing for that renderer

Use HTML controls only in a pipeline that keeps them

Use raw HTML inputs for static checkbox appearance only when the delivery pipeline preserves those elements.

This input describes disabled controls, with the completed task carrying a checked attribute:

| Task | Status |
        | --- | --- |
        | Draft release notes | <input type="checkbox" disabled aria-label="Draft release notes"> |
        | Review schema | <input type="checkbox" disabled checked aria-label="Review schema"> |
        

Our table-enabled markdown-it-py configuration retained those elements when HTML was allowed. Python-Markdown and Pandoc GFM retained them too. Turning HTML off in the matched markdown-it-py configuration kept the table but escaped the tags, leaving their spelling as cell text. Changing the bracket marker would not repair that HTML setting. 10

Output from our HTML-checkbox sample with HTML disabled, unedited:

<tr>
        <td>Draft release notes</td>
        <td>&lt;input type=&quot;checkbox&quot; disabled aria-label=&quot;Draft release notes&quot;&gt;</td>
        </tr>
        <tr>
        <td>Review schema</td>
        <td>&lt;input type=&quot;checkbox&quot; disabled checked aria-label=&quot;Review schema&quot;&gt;</td>
        </tr>
        
Delivery condition What can fail Choice
Raw HTML enabled in a table-capable parser A later host filter can still alter the generated elements Use this route for output whose rendering pipeline you control
Raw HTML disabled Input tags become escaped text Keep status words or use an explicitly supported host feature
Output sent to a hosted Markdown service Local HTML does not establish the published result Follow the host's documented task syntax

GitHub's markup project describes HTML generation, sanitization, and further filters as separate stages. A local Pandoc or markdown-it-py result therefore cannot establish which raw controls GitHub.com will display. We did not test GitHub.com or a VS Code preview for this guide. 6

The disabled controls above are a static rendering choice. Removing that attribute changes whether a browser can operate the control, but does not add code that edits the Markdown source. The GFM specification leaves checkbox interaction to implementers. 1

Use host support when ticks must be saved

Use an application's documented table-task feature when a tick must update the stored document.

GitLab documents a native table-cell task extension: the marker occupies the entire cell, with the task description in a neighboring cell. Its marker-only form is: 5

| Status | Task |
        | --- | --- |
        | [ ] | Draft release notes |
        | [x] | Review schema |
        

GitLab was not executed for this guide. Local Pandoc GFM output does not establish GitLab's table-task behavior. 5

Obsidian's Markdown table checkboxes plugin documents a different approach: it replaces cell markers with HTML controls and reflects view-mode changes in the file. That is plugin behavior, and was not executed for this guide. A Markdown export viewed without the same integration cannot be assumed to retain the interaction. 8

A report in that plugin's issue tracker describes ticks disappearing between views and later visits. The maintainer attributed the reported case to missing checkbox IDs and suggested regenerating them. This is a documented failure report with a proposed fix, not a bug we reproduced or a confirmed diagnosis for every disappearing tick. 9

For a table-task workflow, a concrete acceptance check is to toggle a task, inspect the saved source for its changed state, close the document, and reopen it. If the source never changes, the control's visible state has not established source persistence. GitHub's documented comment task lists are another host-managed route when the work can live outside a table. 78

Use status words or symbols for shared table reports. Reserve raw HTML controls for a pipeline that keeps them, and use explicit host integration for tasks that people must tick and save.

How we tested

These experiments measure local HTML structure, not hosted-app rendering or saved state.

The table-specific experiment ran on 2026-11-04 with Python 3.11.14, markdown-it-py 4.0.0, Python-Markdown 3.10.2, and Pandoc 3.11. It added 30 cases to the existing renderer record, preserving the upstream baseline. Only the table-related cases below inform this guide. 1011

The existing renderer environment was used because it already contained the Markdown libraries. From the repository root, reproduce it with:

PYTHONDONTWRITEBYTECODE=1 .venv/bin/python runs/2026-11-04-markovo-markdown-checkbox-in-table/lab/checkbox_table_probe.py
        
Source file What it represents
table-bare-markers.md Bare [ ] and [x] in a status-only cell
table-list-markers.md Hyphen-prefixed task markers inside cells
table-break-markers.md Task-looking text after an inline HTML break
table-status-text.md Unicode symbols paired with Pending and Done
table-html-inputs.md Disabled raw HTML inputs, including a checked state
outside-task-list.md A standalone task list as the container control

All inputs live under lab/renderers/. The input and output manifest links every source and raw HTML file. Output names follow <sample>--<configuration>.txt; the HTML is saved as text so it remains inspectable evidence. 11

Configuration Exact renderer operation
CommonMark MarkdownIt("commonmark", {"html": True}).render(source)
Table + HTML MarkdownIt("commonmark", {"html": True}).enable("table").render(source)
Table + escaped HTML MarkdownIt("commonmark", {"html": False}).enable("table").render(source)
Python tables markdown.markdown(source, extensions=["tables", "fenced_code"])
Pandoc GFM pandoc -f gfm -t html, with the source supplied on standard input

Each result below is tables / checkbox inputs / checkbox inputs inside tables, copied from results.json. 10

Sample CommonMark Table + HTML Table + escaped HTML Python tables Pandoc GFM
table-bare-markers 0 / 0 / 0 1 / 0 / 0 1 / 0 / 0 1 / 0 / 0 1 / 0 / 0
table-list-markers 0 / 0 / 0 1 / 0 / 0 1 / 0 / 0 1 / 0 / 0 1 / 0 / 0
table-break-markers 0 / 0 / 0 1 / 0 / 0 1 / 0 / 0 1 / 0 / 0 1 / 0 / 0
table-status-text 0 / 0 / 0 1 / 0 / 0 1 / 0 / 0 1 / 0 / 0 1 / 0 / 0
table-html-inputs 0 / 2 / 0 1 / 2 / 2 1 / 0 / 0 1 / 2 / 2 1 / 2 / 2
outside-task-list 0 / 0 / 0 0 / 0 / 0 0 / 0 / 0 0 / 0 / 0 0 / 2 / 0

The probe uses Python's HTMLParser to count start tags, including self-closing input tags once. A checkbox input is an input element whose type attribute is checkbox. It counts checked and disabled attributes separately, and records whether a checkbox has a table ancestor. Decoded cell text excludes tags and attributes. These are structural counts, without visual-quality, OCR, or performance scoring. The PDF scorer does not apply. 11

For table-html-inputs, every configuration retaining HTML emitted a single checked input and disabled both inputs. The CommonMark result placed them outside a table. For outside-task-list, only Pandoc GFM emitted checkbox inputs, with a single checked input and no disabled attribute. The full count table also reports list-item counts. 10

The excerpts came from table-status-text--python-markdown.txt, table-list-markers--markdown-it-table-html.txt, outside-task-list--pandoc-gfm.txt, table-html-inputs--markdown-it-commonmark.txt, and table-html-inputs--markdown-it-table-escaped.txt. Excerpts are contiguous and unedited, including Pandoc's line wrap.

Source inspection covered table() in markdown-it-py's markdown_it/rules_block/table.py and TableProcessor._build_row() in Python-Markdown's markdown/extensions/tables.py. The source record contains the inspected functions and installed-file hashes. The token traces and diagnosis separate direct observations from the inference about missing processing stages. 1213

The GFM reference is version 0.29-gfm, dated 2019-04-06. GitLab's documentation places native table-cell tasks at GitLab 18.9. The Obsidian issue report identifies plugin 3.0.0 with Obsidian 1.9.10 on Android and was opened on 2025-08-26. These document and report boundaries do not establish the behavior of an untested installation. All external references were accessed on 2026-11-04. 159

GitHub.com, GitLab, VS Code, Obsidian, browser interaction, and source write-back were not executed for this guide. Markovo was not a tested renderer. The product link describes a document-cleanup context, with no claim about table-task rendering or state preservation.

Sources

  1. GFM specification, tables and task-list extensions
  2. markdown-it-py README, v4.0.0
  3. Python-Markdown tables extension
  4. Pandoc task_lists extension
  5. GitLab task lists in tables
  6. GitHub Markup rendering pipeline
  7. GitHub tasklists documentation
  8. Obsidian Markdown table checkboxes README
  9. Plugin report about ticks not staying checked
  10. Local renderer results, 2026-11-04
  11. Reproduction script
  12. Source diagnosis and inference boundaries
  13. Inspected parser source and hashes