> Content index: https://devbio.me/blogs/llms.txt
> Canonical page: https://devbio.me/blogs/markdown-checkbox-syntax

---
title: Markdown Checkbox: Why Yours Renders as Text
description: Your markdown checkbox renders as literal text when the space is missing. Here's the exact GFM syntax, why it fails outside GitHub, and how to fix it fast.
keywords: markdown checkbox, markdown checkbox syntax, markdown task list, github markdown checkbox
published: 2026-09-14
updated: 2026-09-14
url: https://devbio.me/blogs/markdown-checkbox-syntax
word_count: 2488
---

# Markdown Checkbox: Why Yours Renders as Text

> Your markdown checkbox renders as literal text when the space is missing. Here's the exact GFM syntax, why it fails outside GitHub, and how to fix it fast.

Canonical: https://devbio.me/blogs/markdown-checkbox-syntax
Published: 2026-09-14

## Related Pages

- [Markdown Emoji: Why :tada: Isn't Markdown](https://devbio.me/blogs/markdown-emoji-shortcodes)
- [Markdown Image Size: The Syntax That Doesn't Exist](https://devbio.me/blogs/markdown-image-size)
- [GitHub Profile README Best Practices: The 82% Gap](https://devbio.me/blogs/github-profile-readme-guide)
- [Markdown Code Block: Syntax, Languages, Nesting](https://devbio.me/blogs/markdown-code-block)
- [Markdown Horizontal Line: Why Yours Became a Heading](https://devbio.me/blogs/markdown-horizontal-line)

When I pulled the top ten Google results for "markdown checkbox" in September 2026, three of them were people asking why theirs doesn't work — an Obsidian forum thread, a Reddit post titled "Simple markdown checkbox doesn't work," and a Logseq feature request asking for GitHub-flavored checkbox support. That's the whole problem in one search page: the syntax is four characters long, and it still fails constantly.

A markdown checkbox is a list item whose text begins with `- [ ]` for unchecked or `- [x]` for checked. The bracket pair has to be the first thing inside the list item, and it must be followed by at least one space. Miss that space and the renderer prints the brackets as literal text instead of drawing a box.

![white spiral notebook on brown wooden table](https://images.unsplash.com/photo-1612367980327-7454a7276aa7?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w4OTM1MDJ8MHwxfHNlYXJjaHwxfHxjaGVja2xpc3QlMjBub3RlYm9vayUyMGRldmVsb3BlciUyMGRlc2t8ZW58MHwwfHx8MTc4OTM4NDIzOXww&ixlib=rb-4.1.0&q=80&w=1080)
*Photo by [Kelly Sikkema](https://unsplash.com/@kellysikkema?utm_source=quillly&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=quillly&utm_medium=referral)*

## Table of Contents

- [The Exact Markdown Checkbox Syntax](#the-exact-markdown-checkbox-syntax)

- [Why Your Checkbox Renders as Literal Text](#why-your-checkbox-renders-as-literal-text)

- [A Markdown Task List Isn't Markdown](#a-markdown-task-list-isnt-markdown)

- [Where Checkboxes Actually Render](#where-checkboxes-actually-render)

- [Clickable or Decorative: It Depends Where You Put It](#clickable-or-decorative-it-depends-where-you-put-it)

- [Nesting, Indentation, and Progress Counters](#nesting-indentation-and-progress-counters)

- [Checkboxes Inside Tables Never Work](#checkboxes-inside-tables-never-work)

- [Frequently Asked Questions](#frequently-asked-questions)

## The Exact Markdown Checkbox Syntax

Here is every valid form, straight from the GitHub Flavored Markdown specification:

```markdown
- [ ] Unchecked task
- [x] Checked task
- [X] Also checked — uppercase X is valid
* [ ] Asterisk bullets work too
+ [ ] So do plus bullets
1. [ ] Ordered lists work as well
```

The spec is precise about what counts. [GFM section 5.3](https://github.github.com/gfm/#task-list-items-extension) defines it this way: "A task list item is a list item where the first block in the contents of the list item is a paragraph which begins with a task list item marker." The marker itself is "an opening bracket (`[`), either a space character, a lowercase `x` character, or an uppercase `X` character, a closing bracket (`])`" — followed by at least one space.

Read that definition twice, because four separate requirements hide inside it:

1. It must be a **list item** — a bullet or a number, then a space.

2. The marker must be the **first block** in that item.

3. That first block must be a **paragraph**, not a heading or a code block.

4. The `]` must be followed by **whitespace**.

Break any one of those and you get literal brackets. That single sentence explains nearly every failure below.

![Comparison card showing valid markdown checkbox syntax with a hyphen, space, bracket, space, bracket, space against three invalid variants that render as literal text](https://quillly.com/serve/v1/019e3623-8b91-738d-ae30-42d5bcd996a0/images/6905479441f2e62f40de38ed6cb8f30d926f32e0.webp)

## Why Your Checkbox Renders as Literal Text

A markdown checkbox renders as literal text when the line isn't a valid list item, when the bracket pair isn't the first thing inside it, or when there's no space after the closing bracket. If all three are correct, your renderer simply doesn't implement GitHub's task list extension.

Five failure modes cover almost everything people hit. Four are typing mistakes; the fifth isn't your fault at all.

**1. No space after the closing bracket.** `- [ ]Buy milk` fails. The spec requires whitespace after the marker, so without it the parser sees ordinary text that happens to contain brackets.

**2. No space between the bullet and the bracket.** `-[ ] Buy milk` fails for a different reason — without the space, `-[` isn't a list marker, so there's no list item for the checkbox to live in.

**3. No list marker at all.** A bare `[ ] Buy milk` on its own line is just a paragraph. Checkboxes only exist inside list items.

**4. The marker isn't first.** `- Today: [ ] Buy milk` fails. The paragraph has to *begin* with the marker. Anything in front of it, even one word, disqualifies the item.

**5. Your renderer doesn't implement the extension.** Your syntax is perfect and it still prints brackets. This is the case that sends people to forums, and it's the interesting one.

![Vertical decision flowchart for diagnosing why a markdown checkbox renders as plain text, checking list marker, marker position, trailing space, and renderer support in turn](https://quillly.com/serve/v1/019e3623-8b91-738d-ae30-42d5bcd996a0/images/aad0a72983f7af4cf20818f5290082b071fd5ab7.webp)

If you've reached the bottom of that flowchart with valid syntax, the tool is the problem. And to understand why so many tools disagree, you have to know where task lists come from.

[Build a developer page that renders the way you expect](https://devbio.me) — no guessing which flavor of markdown you're writing into.

## A Markdown Task List Isn't Markdown

Here's the fact that resolves most of the confusion: a markdown task list is not part of Markdown. It's a GitHub extension.

Before writing this, I searched the [CommonMark specification, version 0.31.2](https://spec.commonmark.org/0.31.2/) — the closest thing Markdown has to a standard — for the words "task", "checkbox", and "tick". Zero hits. Not one. The specification that defines emphasis, lists, code fences, and link references says nothing whatsoever about checkboxes.

Now open the [GFM specification](https://github.github.com/gfm/). Section 5.3 is titled "Task list items (**extension**)" — GitHub's own document flags it as an addition, sitting alongside tables, strikethrough, and autolinks in the extensions chapter.

That's why the Obsidian, Logseq, and Databricks threads exist. Every one of those tools made an independent decision about whether to implement an extension that no standard requires. Some did it fully, some partially, some not at all. The syntax being "broken" in Logseq isn't a bug — it's a tool that implements CommonMark and not GitHub's additions.

This is the same trap as [emoji shortcodes, which aren't Markdown either](https://devbio.me/blogs/markdown-emoji-shortcodes), and the reason [there's no image-size syntax in Markdown](https://devbio.me/blogs/markdown-image-size) despite everyone expecting one. When a feature comes from a vendor rather than a spec, portability stops being a promise.

![Side by side comparison showing CommonMark 0.31.2 has zero mentions of task, checkbox, or tick while GitHub Flavored Markdown defines task list items in section 5.3 as an extension](https://quillly.com/serve/v1/019e3623-8b91-738d-ae30-42d5bcd996a0/images/6cc6ba70fee8f2895dc0d7694871ba6d6d347354.webp)

## Where Checkboxes Actually Render

Checkboxes render on GitHub, GitLab, Obsidian, and most developer-facing Markdown tools. They fail in strict CommonMark renderers, inside table cells, and in the minimal parsers most profile bio fields use. Clickability is a separate question: only issue and comment surfaces make the boxes interactive.

Support splits into three tiers, and knowing which tier you're writing for saves a lot of debugging.

| Where you're writing | Checkbox renders? | Clickable? |
| --- | --- | --- |
| GitHub issue, PR, or comment | Yes | Yes |
| GitHub README or `.md` file | Yes | No |
| GitLab issues and Markdown files | Yes | Yes, in issues |
| Obsidian | Yes | Yes |
| VS Code Markdown preview | Yes | No |
| Databricks notebook Markdown cells | Often not | No |
| Strict CommonMark renderers | No | No |
| A short bio field on most profile sites | No | No |

![Three tiers of markdown checkbox support showing full support with clickable boxes on GitHub issues, render-only support in README files and VS Code preview, and no support in strict CommonMark renderers and minimal bio field parsers](https://quillly.com/serve/v1/019e3623-8b91-738d-ae30-42d5bcd996a0/images/5f52499b5ca20f6a39fba82ac94d3daf990bb331.webp)

That last row is worth expanding, because it catches people constantly. Profile and bio surfaces usually run a deliberately minimal renderer rather than a full Markdown engine. When I checked how devbio.me renders its own About section, I found it supports exactly three things — `**bold**`, `*italic*`, and the link form `[label](address)`. There's no list parsing in it at all, so a `- [ ]` line renders as the literal characters you typed. That's a deliberate tradeoff: a full Markdown parser is a large dependency and a much bigger surface for untrusted input, which is a poor trade for a few paragraphs of bio text.

The lesson generalizes. Before you write a checkbox into any field, ask what's actually parsing it. A README is parsed by GitHub's full GFM pipeline. A bio field is usually parsed by fifty lines of regex.

## Clickable or Decorative: It Depends Where You Put It

Rendering and interactivity are two different questions, and GitHub treats them differently by context.

In an issue, pull request, or comment, checkboxes are live controls. [GitHub's documentation](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/about-task-lists) describes selecting and deselecting boxes to mark tasks complete, with progress surfacing "in various places on GitHub, such as a repository's list of issues." Clicking one edits the underlying comment body for you, swapping `[ ]` for `[x]` and recording it in the edit history.

In a Markdown file — a README, a `CONTRIBUTING.md`, a doc page — the box renders but there's nothing to click. The file is version-controlled content, so the only way to change a checkbox is to edit the file and commit the change. GitHub's docs scope the interactive behavior to comments and issues for exactly this reason.

This matters for [README structure](https://devbio.me/blogs/github-profile-readme-guide). A roadmap of checkboxes in a README looks like a live tracker and behaves like a static image. If you want readers to actually tick things off, that content belongs in an issue. If you just want to show what's done, a README checklist is fine — just don't expect anyone to interact with it.

One more limitation from GitHub's own docs: you can't create tasklist items inside closed issues, or in issues that have linked pull requests.

If you want a roadmap readers can take in at a glance, [put it somewhere you control](https://devbio.me) rather than hoping a README checklist reads as live.

## Nesting, Indentation, and Progress Counters

Nested checkboxes follow normal list indentation. Indent the child items and they nest:

```markdown
- [x] Ship v2
  - [x] Write migration guide
  - [ ] Update the changelog
  - [ ] Tag the release
- [ ] Announce
```

Two things surprise people here. First, checking a parent does **not** check its children, and checking every child does not check the parent — each box is independent text, and nothing computes a rollup for you. Second, indentation must be enough to make the item a child of the one above; two spaces is the reliable choice, and a single space often isn't.

Mixed lists work fine too. You can put a plain bullet and a checkbox in the same list, because a task list item is just a list item with a special opening:

```markdown
- [ ] This one has a box
- This one doesn't
- [x] This one is checked
```

If your indentation collapses or the nesting flattens, the cause is usually the same one that breaks [code blocks inside list items](https://devbio.me/blogs/markdown-code-block): inconsistent indentation width between levels.

## Checkboxes Inside Tables Never Work

The fourth-ranked result for "markdown checkbox" is a Stack Overflow question asking how to draw a checkbox inside a GitHub Markdown table. It's the most common advanced version of this question, and the answer is that you can't — not with task list syntax.

The reason is structural. In GFM, the contents of a table cell are parsed as **inline** content. A task list item requires a list item, which is a block-level construct. Blocks don't exist inside table cells, so `- [ ]` in a cell renders as the literal characters.

The workaround is to stop asking for a checkbox and just ask for a box-shaped character:

```markdown
| Task | Done |
| --- | --- |
| Write the docs | &#9745; |
| Ship the release | &#9744; |
```

`&#9744;` is ☐ (ballot box) and `&#9745;` is ☑ (ballot box with check). Both are plain Unicode, so they render everywhere — including in strict CommonMark, where real task lists don't exist at all. You can also use `:white_check_mark:` on GitHub surfaces, though that relies on [emoji shortcodes, which carry their own portability problem](https://devbio.me/blogs/markdown-emoji-shortcodes).

Unicode characters have one real advantage over task lists: they survive being copied anywhere. The tradeoff is that nothing can ever make them interactive.

> **Stop debugging which markdown flavor you're in**
> A developer page that renders your links, projects, and live stats without a parser lottery — one URL you own.
> → [Claim your devbio page](https://devbio.me)

## What to Do When a Checkbox Won't Render

Check four things in order: that the line opens with a list marker and a space, that the bracket pair comes first in the item, that a space follows the closing bracket, and that the brackets hold only a space, `x`, or `X`. If all four pass, the renderer is the problem.

Work this list top to bottom and you'll find it in under a minute:

- Confirm the line starts with `-`, `*`, `+`, or `1.` followed by a space.

- Confirm there's exactly one space between the bullet and `[`.

- Confirm there's a space after `]`.

- Confirm nothing precedes the bracket pair in that list item.

- Confirm the brackets contain a space, `x`, or `X` — nothing else. `[✓]` and `[-]` are not valid markers.

- Check whether you're inside a table cell. If so, switch to `&#9744;` / `&#9745;`.

- Check whether the tool implements GFM. If it doesn't, no amount of correct syntax will help.

If you're writing for a surface you don't control, the safest choice is always the Unicode character. It isn't interactive, but it never silently degrades into brackets — the same reasoning behind using [real horizontal rules instead of fragile syntax](https://devbio.me/blogs/markdown-horizontal-line) when portability matters.

## Frequently Asked Questions

### Is `- [X]` with a capital X valid?

Yes. The GFM specification explicitly allows "a lowercase `x` character, or an uppercase `X` character" inside the brackets. Both render as a checked box, and they're interchangeable. Any other character — a checkmark, a dash, a letter — is not a valid marker and renders as literal text.

### Why does my markdown checkbox work on GitHub but not in my notes app?

Because task lists are a GitHub extension, not standard Markdown. CommonMark 0.31.2 never mentions checkboxes. Each editor decides independently whether to implement GFM's extensions, so identical syntax renders as a box in one tool and as brackets in another.

### Does a GitHub markdown checkbox work in a README?

It renders as a checkbox, but you can't click it. GitHub scopes interactive checkboxes to issues, pull requests, and comments, where clicking rewrites the stored text. A README is version-controlled, so changing a box there means editing the file and committing.

### How do I put a checkbox in a Markdown table?

You can't use task list syntax — table cells hold inline content only, and a task list needs a list item. Use the Unicode characters instead: `&#9744;` for an empty box and `&#9745;` for a checked one. They render in every Markdown tool, including strict CommonMark.

### Do checked boxes update a progress counter automatically?

In GitHub issues, yes — completed tasks roll up into progress indicators on the repository's issue list. In Markdown files, no. Nothing counts your boxes or computes a percentage; a README checklist is static text that happens to look like a tracker.

### Can I nest checkboxes under a parent task?

Yes, using normal list indentation — two spaces per level is reliable. But each box is independent. Checking a parent won't check its children, and completing every child won't check the parent. There's no rollup logic anywhere in the spec.

## Key Takeaways

A markdown checkbox is `- [ ]` or `- [x]`, and it needs four things at once: a list marker, a space, the bracket pair at the very start of the item, and a space after the closing bracket. Miss any one and you get literal brackets.

When the syntax is right and it still fails, you've hit the real issue — task lists aren't Markdown. CommonMark 0.31.2 doesn't mention them at all, and GFM files them under section 5.3 as an extension. Every tool chooses whether to implement that, which is why the same four characters render three different ways across GitHub, Obsidian, and Logseq.

Write checkboxes where a GFM parser is guaranteed. Everywhere else — table cells, bio fields, strict CommonMark — reach for `&#9744;` and `&#9745;` and keep the rendering predictable. If you want a profile page where you know exactly what renders, [devbio.me gives you one link you own](https://devbio.me) with the parsing rules documented up front.
