Markdown Checkmark: Symbols, Entities and Codes That Work (2026)
TL;DR: Markdown has no checkmark syntax. A ✓ reaches a rendered file one of exactly three ways: paste the literal character, write an HTML entity (
✓or✔), or type an emoji shortcode (:white_check_mark:). Only the literal character works everywhere. And✓,✓and✗are the only named entities in the entire HTML list that resolve to a check or a cross.
A markdown checkmark is a character you type, not markup the parser understands. Neither the CommonMark spec nor GitHub Flavored Markdown defines anything that turns into a ✓. Every checkmark in a .md file got there by one of three routes, and the route decides whether the mark comes out monochrome, green, or as the literal text ✓.
Looking for the tickable box - [x] in a task list? That is a different feature, covered in the markdown checkbox guide. This page is about the glyph itself.
Every code point, entity and shortcode below was checked on 26 July 2026 against the WHATWG entity list, the Unicode character database, GitHub's Markdown API and github/gemoji. If you read a lot of .md files on macOS, MacMD Viewer is the app I build for it. For syntax this page skips, see the markdown cheat sheet.
How Do You Add a Markdown Checkmark?
Paste the character, write an HTML entity, or type an emoji shortcode. Those are the three routes, and they are handled by three different things at three different moments.
| Route | What you write | What handles it | Where it works |
|---|---|---|---|
| Literal Unicode | ✓ pasted into the source | Nothing; Markdown passes the character through | Everywhere |
| HTML entity | ✓ or ✔ | The Markdown parser, at parse time | Renderers with an HTML pipeline |
| Emoji shortcode | :white_check_mark: | The platform, layered on top of Markdown | GitHub, Slack, Notion |
The literal character is the only route with no dependency: it survives copy-paste and no parser needs to know anything about it. The reference table below is there to be copied from.
The entity route is the one people misunderstand. ✓ is not decoded by your browser after the fact; the Markdown parser decodes it and emits a real ✓ into the HTML. That is why it works in a static site generator, a README and a local viewer alike, and fails in a chat app that never produces HTML. The shortcode route is not Markdown at all: each platform ships its own list of names on top of the parser.
Markdown Checkmark Reference Table
Copy the symbol from the first column, or copy whichever code the platform you are writing for accepts. Unicode names come from UnicodeData.txt, named entities from the WHATWG list, shortcodes from github/gemoji, and the presentation column from Unicode's emoji-data.txt.
Check marks and boxes
| Symbol | Unicode name | Code point | Named entity | Decimal | Hex | GitHub shortcode | Default presentation |
|---|---|---|---|---|---|---|---|
| ✓ | CHECK MARK | U+2713 | ✓ ✓ | ✓ | ✓ | none | Text always (not an emoji) |
| ✔ | HEAVY CHECK MARK | U+2714 | none | ✔ | ✔ | :heavy_check_mark: | Text by default, emoji with U+FE0F |
| ✅ | WHITE HEAVY CHECK MARK | U+2705 | none | ✅ | ✅ | :white_check_mark: | Emoji (color, no selector needed) |
| ☑ | BALLOT BOX WITH CHECK | U+2611 | none | ☑ | ☑ | :ballot_box_with_check: | Text by default, emoji with U+FE0F |
| ☐ | BALLOT BOX | U+2610 | none | ☐ | ☐ | none | Text always (not an emoji) |
Cross marks and X marks
| Symbol | Unicode name | Code point | Named entity | Decimal | Hex | GitHub shortcode | Default presentation |
|---|---|---|---|---|---|---|---|
| ✗ | BALLOT X | U+2717 | ✗ | ✗ | ✗ | none | Text always (not an emoji) |
| ✘ | HEAVY BALLOT X | U+2718 | none | ✘ | ✘ | none | Text always (not an emoji) |
| ☒ | BALLOT BOX WITH X | U+2612 | none | ☒ | ☒ | none | Text always (not an emoji) |
| ❌ | CROSS MARK | U+274C | none | ❌ | ❌ | :x: | Emoji (color) |
| ❎ | NEGATIVE SQUARED CROSS MARK | U+274E | none | ❎ | ❎ | :negative_squared_cross_mark: | Emoji (color) |
| ✖ | HEAVY MULTIPLICATION X | U+2716 | none | ✖ | ✖ | :heavy_multiplication_x: | Text by default, emoji with U+FE0F |
One naming trap in that table sends people hunting for a character that does not exist. The formal Unicode name of ✅ is WHITE HEAVY CHECK MARK, but the mark is green on every major platform and its emoji name is "check mark button". "White" describes the hollow outline in the original Dingbats design, not the color.
Why Do ✔ and ✔️ Render Differently?
Because they are not the same string. ✔ is one code point, U+2714. ✔️ is two: U+2714 followed by U+FE0F, the emoji presentation selector. The second one is a request for color.
I confirmed this against GitHub's own renderer by posting to its Markdown API and dumping the exact code points that came back:
| Written in Markdown | GitHub output | Code points returned | Renders as |
|---|---|---|---|
✔ | ✔ | U+2714 | monochrome text glyph |
:heavy_check_mark: | ✔️ | U+2714 U+FE0F | color emoji |
Same character, two different results: the shortcode route silently appends the selector, the entity route does not. That explains most reports that a checkmark "looks different on GitHub than in my editor". The github/gemoji database stores the payload with the selector baked in:
['white_check_mark'] ['0x2705']
['heavy_check_mark'] ['0x2714', '0xfe0f']
['ballot_box_with_check'] ['0x2611', '0xfe0f']
UTS #51, the Unicode emoji standard, defines both selectors: U+FE0E VARIATION SELECTOR-15 is the text presentation selector, and U+FE0F VARIATION SELECTOR-16 the emoji presentation selector, "used to request an emoji presentation for an emoji character". Only some characters accept them, and these are the registered sequences for the marks above:
2611 FE0E ; text style 2611 FE0F ; emoji style BALLOT BOX WITH CHECK
2705 FE0E ; text style 2705 FE0F ; emoji style WHITE HEAVY CHECK MARK
2714 FE0E ; text style 2714 FE0F ; emoji style HEAVY CHECK MARK
2716 FE0E ; text style 2716 FE0F ; emoji style HEAVY MULTIPLICATION X
274C FE0E ; text style 274C FE0F ; emoji style CROSS MARK
274E FE0E ; text style 274E FE0F ; emoji style NEGATIVE SQUARED CROSS MARK
✓ U+2713 and ✗ U+2717 appear nowhere in that file. They have no variation sequences because they are not emoji at all, which turns out to be the most useful fact on this page.
Which Checkmark HTML Entities Actually Exist?
Three names, two characters. That is the complete list:
✓ → U+2713 ✓
✓ → U+2713 ✓
✗ → U+2717 ✗
The WHATWG named character reference list contains 2,231 named references, 2,125 of which require the trailing semicolon. Exactly two characters among them are a check or a cross. There is no named entity for ✔, ✅, ☑, ❌ or ✖, so those need a numeric reference or the literal character.
That matters because entity names are not guessable and unrecognized ones are never decoded. &heavycheck;, &tick; and &checkbox; appear on plenty of tutorial pages and none exist; write one and the renderer leaves it on the page as literal text.
CommonMark treats the WHATWG list as a normative dependency and says so in the spec: "The document https://html.spec.whatwg.org/entities.json is used as an authoritative source for the valid entity references and their corresponding code points." Numeric references have a fixed shape. Decimal is &# plus one to seven digits plus a semicolon; hex is &# plus x or X plus one to six hex digits plus a semicolon.
Should You Use ✓, ✔ or ✅?
Use ✓ U+2713 when you need the mark to behave like text, and ✅ U+2705 when you want a green badge. The reason is a property, not a style preference.
UTS #51 draws the line: "a text presentation is a simple foreground shape whose color is determined by other information, such as setting a color on the text, while an emoji presentation determines the color(s) of the character, and is typically multicolored." A text-presentation mark inherits your CSS color, prints in black and white and sits at the right weight in a table; an emoji ignores all of that and draws itself.
A middle category causes most of the trouble. In Unicode Emoji 17.0 (Unicode Consortium, 2025), 219 of the 1,438 code points carrying Emoji=Yes are text-default: they have Emoji_Presentation=No, so they render as monochrome text unless followed by U+FE0F. ✔ U+2714, ☑ U+2611 and ✖ U+2716 are three of them. But text-default is not text-only: a platform may render them in color anyway, and messaging apps commonly do, since UTS #51 says presentation depends on the environment.
So for a guaranteed monochrome mark, use ✓ U+2713 or ✗ U+2717, the marks with no emoji property at all — no conforming platform will give them an emoji presentation. For the green badge, use ✅, which defaults to color with no selector needed.
Which Platforms Render a Markdown Checkmark?
The literal Unicode character is the only route that works on all of them. Entities need an HTML pipeline; shortcodes need a platform that ships an emoji list.
| Platform | Literal ✓ / ✅ | HTML entity | Emoji shortcode |
|---|---|---|---|
| GitHub (Markdown, issues, README) | Yes | Yes, named and numeric | Yes |
| Slack | Yes | No, there is no HTML pipeline | Yes |
| Discord | Yes | Not documented | Widely used, not documented |
| Obsidian | Yes | Yes, HTML is supported in notes | Not documented in core |
| Notion | Yes | Not documented | Yes, by typing : |
| MacMD Viewer | Yes | Yes, named and numeric | No |
GitHub is the row I verified end to end, pushing all twelve inputs through its Markdown API and comparing the returned code points. Slack's documented formatting set is bold, italic, underline, strikethrough, code, blockquote, code block and lists, with no HTML in it, though Slack does expand emoji codes typed with a leading colon. It ships its own emoji set and workspaces can add custom aliases, so do not assume GitHub's shortcode names carry over.
Discord is the honest gap here. Its parser has no HTML pipeline, so entities almost certainly do not work, but I could not reach an official page saying so; for what is verified about that platform, see the Discord markdown guide. Obsidian supports HTML in notes and its own documentation uses entities as a formatting device; shortcode expansion there is plugin territory.
Four Reasons a Markdown Checkmark Fails to Render
Every failure I have seen falls into one of these four, and only the last is about fonts.
It is inside backticks. CommonMark states that "entity and character references are not recognized in code blocks and code spans", and GitHub honors it exactly. Sending inline `✓` and `:white_check_mark:` versus plain ✓ through its API returns <code>&check;</code>, <code>:white_check_mark:</code>, and a real ✓ for the one outside the backticks. If you demo an entity inside a code span and conclude it is broken, that is the code span doing its job.
The entity name does not exist. Unrecognized names are not decoded; they render as the literal text you typed, so &tick; gives you &tick; on the page.
The shortcode is on a platform that does not expand it. :white_check_mark: is a github.com feature, not part of GFM. Render the file with a plain CommonMark or GFM parser and you get the literal string back.
The character is right, the presentation is not. You get a mark, just not the one you pictured: black where you expected green, or a color emoji where you wanted a text glyph. Choose the code point deliberately from the presentation column above.
Task List Checkboxes Are a Different Feature
The - [x] syntax does not produce a ✓ character. It produces a rendered checkbox element, and GFM accepts only a space, a lowercase x or an uppercase X between the brackets, so - [✅] and - [✓] are not task-list variants; they fail to parse as one. Use a checkmark character when you want a mark inside a sentence, a table cell or a heading, and a task list when you want a tickable box with completion state. For clickable task lists, the markdown checkbox guide covers the syntax, the nesting rules and the four spacing mistakes that break it.
How Do You Preview Markdown Checkmarks on a Mac?
macOS ships no Markdown renderer, so TextEdit shows ✓ as raw text and gives you no way to tell a text glyph from an emoji before you publish.
VS Code — open the .md file and press Cmd+Shift+V for the built-in preview, which uses a CommonMark-compatible renderer and decodes entities the same way.
MacMD Viewer — the native SwiftUI app I build. I probed the parser version it ships with. Named and numeric entities decode identically to GitHub: ✓, ✓ and ✓ all produce U+2713, ✔ produces U+2714, ✗ produces U+2717. Invalid names like ¬anentity; stay literal, which is correct CommonMark behavior and again matches GitHub. Literal Unicode passes through unchanged. Emoji shortcodes are not expanded, so :white_check_mark: displays as literal text; that expansion is a github.com layer rather than part of GFM. In MacMD, paste the character or use an entity. You also get QuickLook preview in Finder (press Space on any .md file), Mermaid diagrams, syntax highlighting and live reload. Download MacMD Viewer, $19.99 one-time.
Terminal with Glow — brew install glow, then glow README.md renders Markdown in the terminal.
For a wider comparison, see the best markdown viewer guide.
Frequently Asked Questions
How do you add a checkmark in Markdown?
Three routes, none of them Markdown syntax. Paste the literal character (✓ ✔ ✅), write an HTML entity such as ✓ or ✔, or type an emoji shortcode such as :white_check_mark:. Only the literal character works everywhere: entities need an HTML pipeline, shortcodes need a platform that ships an emoji list.
Does the HTML entity ✓ work in Markdown?
Yes, wherever the renderer has an HTML pipeline. CommonMark 0.31.2 decodes named character references at parse time using the WHATWG entity list, so ✓ becomes a real ✓ in the output HTML on GitHub, in Obsidian and in MacMD Viewer. It does not work in Slack, whose formatting set contains no HTML.
Is there an HTML entity for ✔?
No named one. The WHATWG list contains exactly two checkmark names, ✓ and ✓, both mapping to U+2713, plus ✗ for U+2717. For ✔ U+2714, ✅ U+2705, ☑ U+2611 and ❌ U+274C you need a numeric reference such as ✔ or the character itself. &heavycheck; and &tick; do not exist.
Why does my checkmark look different on GitHub than in my editor?
Because the shortcode route silently adds a character. GitHub renders :heavy_check_mark: as U+2714 followed by U+FE0F, the emoji presentation selector, which produces a color emoji. A pasted ✔ is U+2714 on its own and stays a monochrome text glyph.
What is the difference between ✓ and ✅?
Two different code points with different behavior, not one character in two styles. ✓ is U+2713 CHECK MARK, which has no emoji property at all in Unicode and renders as text inheriting the surrounding color. ✅ is U+2705, an emoji that renders in color by default with no variation selector, and green on every major platform despite the formal Unicode name calling it "white".
Why does my ✓ show up as literal text?
It is almost certainly inside backticks. CommonMark states that entity and character references are not recognized in code blocks and code spans, so an entity in an inline code span stays literal on every conforming renderer, GitHub included. The other cause is a name that does not exist: unrecognized names are never decoded, so &tick; renders as &tick;.
Can you put a checkmark inside a Markdown task list?
No. GFM accepts only a space, a lowercase x or an uppercase X between the brackets, so - [✅] and - [✓] fail to parse as task lists and render as literal brackets. A task list produces a rendered checkbox element with completion state; a checkmark is a character you type into the text.
Content licensed under CC BY 4.0. Cite with attribution to MacMD Viewer.