Markdown Checkboxes and Task Lists: The Complete Guide
How to write a markdown checkbox with dash-bracket syntax, where task lists render and where they don't, how to nest them, and the small mistakes that quietly break them.
Short answer: A markdown checkbox is a list item that begins with - [ ] for unchecked or - [x] for checked. The space inside the brackets is required, and so is the space after the closing bracket — - [ ] Buy milk. Task lists are a GitHub Flavored Markdown extension, so they render on GitHub, GitLab, Obsidian, and most modern editors, but not in strict CommonMark and not in Discord or Reddit.
Task lists are the feature that turns a markdown file into something you actually work from. A README becomes a setup checklist. A pull request description becomes a review gate. A daily note becomes a to-do list you can tick off. The syntax is four characters long, which is exactly why the failure mode is so annoying: get one space wrong and you get a bullet with literal square brackets in it, with no error message explaining why.
Here's the full syntax, where it works, and every way it breaks.
The Syntax
- [ ] Unchecked task
- [x] Completed task
- [X] Also completed — uppercase X works too
Which renders as:
- Unchecked task
- Completed task
- Also completed — uppercase X works too
The rules, precisely:
- It has to be a list item. Start the line with
-,*, or+followed by a space. All three bullet markers work. - The checkbox comes first.
[ ]or[x]must be the very first thing in the list item's content. You can't put a checkbox in the middle of a line. - A space inside the brackets for unchecked.
[ ], not[]. - No space inside for checked.
[x], not[ x ]. - A space after the closing bracket.
- [ ] Task, not- [ ]Task.
Ordered lists work too in most parsers that support the extension:
1. [x] Draft the spec
2. [ ] Review with the team
3. [ ] Ship it
GitHub renders ordered task lists as checkboxes. Some parsers only implement the extension for bullet lists, so if the output looks wrong in a tool you don't control, fall back to -.
It's GFM, Not CommonMark
This trips people up constantly. The original Markdown spec from 2004 has no concept of a checkbox. Neither does CommonMark, the standardization effort that most modern parsers implement as their core. Task lists came from GitHub, and they live in the GitHub Flavored Markdown spec as an extension called "task list items."
Practically, that means support is a per-tool question, not a "markdown" question. A strict CommonMark renderer will happily give you a bullet list where each item starts with a literal [ ].
| Where | Renders as checkbox | Clickable |
|---|---|---|
| GitHub — issues, PRs, comments | Yes | Yes, and clicking edits the source |
GitHub — .md files in a repo | Yes | No, static |
| GitLab — issues, MRs, comments | Yes | Yes |
| Obsidian | Yes | Yes |
| VS Code preview | Yes | No |
| Pandoc | Yes, with the task_lists extension | N/A |
| Static site generators | Only with a GFM plugin (remark-gfm, markdown-it-task-lists) | No |
| Discord | No — renders as literal brackets | No |
| No | No | |
| Slack | No — Slack uses its own limited syntax | No |
| Notion | Converts to native to-do blocks on paste/import | Yes, as Notion blocks |
| OpenMark | Yes | Yes, writes back to the file |
If you're writing for a platform not on this list, paste three lines into a preview and check before you commit to the format.
Nested Task Lists
Indent a task under another task to create a sub-list. Two spaces is enough for GitHub and most parsers:
- [ ] Launch checklist
- [x] Write release notes
- [x] Bump version numbers
- [ ] Upload screenshots
- [ ] Submit for review
- [ ] Post-launch
- [ ] Reply to reviews
Which renders as:
- Launch checklist
- Write release notes
- Bump version numbers
- Upload screenshots
- Submit for review
- Post-launch
- Reply to reviews
Two things worth knowing:
Parents don't auto-check. Checking every child leaves the parent unchecked. No markdown parser tracks that relationship — it's a text format, not a task manager.
Strict parsers want the content column. The safe indentation is however many characters precede the parent's text. For - that's two spaces. For 10. that's four. GitHub is forgiving here; some other parsers are not, and under-indenting silently produces a flat list instead of a nested one.
Task Lists Inside Tables Don't Work
This is the most common thing people try that has no clean answer. A GFM pipe table cell holds inline content only — emphasis, code spans, links, images. Lists are block-level, so - [ ] inside a cell renders as literal text:
| Task | Status |
|------|--------|
| Write docs | - [ ] |
That produces a cell containing the characters - [ ]. Not a checkbox.
Raw HTML doesn't rescue you either, at least not on GitHub: <input type="checkbox"> isn't on GitHub's HTML allowlist, so the sanitizer strips it and you're left with an empty cell.
The workarounds:
- Unicode symbols.
☐and☑(or✅and⬜) render everywhere and cost nothing. They're not interactive, but neither is a table. - Move the checklist out of the table. Put the task list above or below it.
- Use a status word.
Done/Pendingin a column is more readable in a table anyway, and it survives every renderer.
For everything else tables can and can't do, see the markdown tables syntax guide.
Progress Counters and Interactive Checkboxes on GitHub
Task lists in a GitHub issue or pull request body aren't just formatting. GitHub parses them and shows a completion counter on the issue — the number of checked items over the total — and that counter follows the issue into list views and project boards. Checking a box in the rendered issue updates the underlying markdown for everyone.
A few behaviors worth knowing:
- Clicking works in issues, PRs, and comments, but not in repository files. A task list in a
README.mdrenders as checkboxes but they're read-only in the repo view. To change one, you edit the file. - You need write access to the repo to tick a box in an issue.
- A task item that is just an issue reference —
- [ ] #412— renders as that issue's title, and stays in sync with whether the issue is closed. - Leading parentheses need escaping.
- [ ] (Optional) Add testsconfuses the parser; write- [ ] \(Optional) Add tests.
More GitHub-specific markdown behavior is covered in markdown for GitHub.
Common Mistakes
| What you typed | What renders | Fix |
|---|---|---|
-[ ] Task | A paragraph of literal text — with no space, it isn't a list item at all | - [ ] Task |
- [] Task | Bullet with literal [] | Put a space inside: - [ ] |
- [ ]Task | Bullet with literal [ ]Task | Space after the bracket |
- [ x ] Task | Bullet with literal brackets | - [x] Task |
– [ ] Task | Nothing list-like — macOS smart dashes turned -- or a hyphen into an en dash | Turn off smart dashes, or retype the hyphen |
| Four-space-indented first item | A code block | Remove the indentation |
- [ ] (Optional) thing | Broken item on GitHub | Escape it: \(Optional) |
| Checkboxes in Discord or Reddit | Literal brackets | Not supported; use bullets or emoji |
The smart-punctuation one deserves emphasis. If you draft in Notes, Mail, or any app with substitutions enabled and paste into a .md file, your hyphens may arrive as en dashes and your quotes as curly quotes. The markdown looks right and parses as nothing.
Clickable Checkboxes in a Real Editor
Static rendering is fine for reading. It's less fine when the file is your to-do list.
OpenMark renders task lists in Document view as real, clickable checkboxes. Click one and it writes the change straight back to the source file — [ ] becomes [x] on disk, and the file stays plain text you can commit, sync, or open in anything else. Switch to Markdown view when you want to edit the text itself.
That combination is the point of task lists in the first place: a checklist you can act on that's still just a text file. No database, no proprietary format, no sync service required. If you keep daily notes or project checklists as markdown, see how to preview markdown on Mac for the other options and how they compare.
Related Syntax
Task lists sit next to a few other features that behave in unexpected ways:
- Markdown line breaks — why a newline inside a task item doesn't do what you expect
- Markdown blockquotes — including callouts, which can contain task lists
- Markdown cheat sheet — the rest of the syntax on one page
Download OpenMark → — $9.99, one-time, native macOS. Task lists render as checkboxes you can actually click, and every tick is saved right back into your markdown file.