What GFM is, and how it became the standard
- GFM is GitHub's dialect of Markdown, published in 2017 as a strict superset of CommonMark.
- The spec itself adds five things: tables, task lists, strikethrough, autolinked URLs and a raw-HTML filter.
- Alerts, @mentions, emoji codes, footnotes and Mermaid come from GitHub's renderer, not from the spec.
- Write GFM by default. Where it is unsupported it falls back to plain text instead of breaking the page.
GitHub Flavored Markdown - GFM for short - Is the dialect GitHub uses everywhere on its platform. READMEs, issues, pull requests, comments, wikis and gists all run on it. GFM keeps the original Markdown syntax and adds the features technical writers kept asking for: tables, task lists, strikethrough and automatic linking of bare URLs.
How GFM became the default
The history explains the reach. GitHub launched in 2008 and adopted Markdown in 2009, back when the 2004 syntax description was loose enough that two parsers could render one file two different ways. GitHub layered its own extensions on top. Millions of developers wrote READMEs every day, and GitHub's version quietly became the version everyone learned.
In 2017 GitHub pinned it all down and published the GFM Spec. It is a formal, heavily tested document, defined as a strict superset of CommonMark. Every valid CommonMark file renders identically under GFM. GFM simply adds a fixed, documented set of extensions on top of it.
Where GFM works besides GitHub
A precise spec plus the world's largest code host is a hard combination to beat. The core extensions now turn up almost everywhere people write:
- Code hosts: GitHub, GitLab and Bitbucket.
- Editors and notes: Obsidian, Typora and VS Code's preview.
- Chat and wikis: Discord and Notion.
- Publishing: Hugo and most modern static site generators.
So when someone says "just write it in Markdown", they almost always mean GFM. If Markdown itself is new to you, read the complete Markdown guide first and come back. If you are still deciding which layer to write in at all, Markdown vs HTML settles that question properly.
Tables: structured data in plain text
Tables are the extension people miss most in plain Markdown. Pipes (|) separate the columns. A divider row of hyphens splits the header from the body. Colons inside that divider row set the alignment.
| Feature | Original MD | GFM |
|:--------------|:-----------:|----:|
| Tables | No | Yes |
| Task lists | No | Yes |
| Strikethrough | No | Yes |
How the divider row controls alignment
One row does all the work. Read each colon as a magnet pulling the text toward it:
:---or a plain---aligns the column left. That is the default.:---:centers it, which suits short status words like Yes and No.---:pushes it right. That is what you want for numbers and prices.
The pipes do not have to line up on screen. The parser only checks that they are there. Neat source is still worth the extra spaces, because the next person to edit the file has to read it as raw text.
Where GFM tables stop
The syntax is deliberately small. There are no merged cells and no nested tables, and a cell cannot hold a real line break. Escape a literal pipe as \| or it will be read as a column edge. For a break inside a cell, use a <br> tag instead.
Tables are also the construct that breaks most often in practice. The Markdown tables guide covers the escaping rules, generators, and the point where an HTML table is simply the better tool.
Task lists: checkboxes in your documents
A task list is an ordinary bulleted list whose items begin with [ ] for an open task or [x] for a finished one. Two spaces decide whether it works: one inside the empty brackets, one after the closing bracket.
## Release checklist
- [x] Update the changelog
- [x] Bump the version number
- [ ] Tag the release
- [ ] Publish release notes
- [ ] Nested subtasks work too
Clickable checkboxes on GitHub
The real prize is that task lists can be interactive. In issues, pull request descriptions and comments, the rendered boxes are clickable. Tick one and GitHub edits the underlying Markdown for you. It also shows a running count, such as "3 of 5 tasks", in issue lists and wherever the pull request is referenced. Notion, Obsidian and many to-do apps treat the same syntax as live checkboxes.
Read-only checkboxes everywhere else
In static output the boxes still render, they just cannot be clicked. That covers a README, a blog post and an exported HTML file. A published checklist is still the clearest thing you can hand a reader, because they can copy the source and reuse it.
Subtasks follow the normal nesting rules, so the normal nesting traps apply. Indent the child item under the text of its parent, and never mix tabs with spaces. The Markdown lists reference spells out how many spaces each renderer expects.
Strikethrough, autolinks and HTML filtering
Three smaller extensions finish off the spec. Two of them add syntax. The third takes something away, on purpose.
Strikethrough with double tildes
Strikethrough wraps text in two tildes, so ~~like this~~ renders as crossed-out text. It is the honest way to show a correction: ~~$99~~ $79 keeps the old price in view. It also marks a line as done without deleting what it said.
Autolinks for bare URLs and emails
Autolinks save you the bracket dance. In original Markdown, a bare address such as https://mdeditor.tw is inert text unless you wrap it in angle brackets or full link syntax. GFM spots bare web and email addresses and makes them clickable on its own.
That is a gift in issue comments and a small trap in polished documents. A raw URL tells the reader nothing about where it leads, so descriptive link text like [MD Editor](https://mdeditor.tw) still reads better.
Disallowed raw HTML
The raw-HTML filter is the one extension that removes rather than adds. Markdown has always let you drop HTML straight into a document, which is a large part of what the Markdown and HTML comparison is about. On a site where millions of strangers write comments, that freedom is a security hole.
So the GFM spec filters a fixed list of dangerous tags: <script>, <title>, <textarea>, <style>, <iframe>, <xmp>, <noembed>, <noframes> and <plaintext>. It escapes the opening bracket, so the tag shows up as text instead of running. Ordinary presentational HTML such as <br>, <sub> and <details> still works fine.
Watch out: This filter is why a video embed that works on your own blog disappears from a GitHub README. Nothing warns you. The <iframe> is simply not there in the rendered page.
GitHub-specific extras: alerts, mentions, footnotes, Mermaid
Beyond the formal GFM spec, GitHub's own renderer adds a layer of platform features. Some of them only work on GitHub. Others, like footnotes and Mermaid, now travel surprisingly well.
Alerts: five colored callout boxes
Alerts are callouts built out of blockquote syntax. The first line of the quote names the type, and GitHub gives each one its own color and icon:
> [!WARNING]
> This action permanently deletes your data.
[!NOTE]- Useful information a reader should not skip.[!TIP]- An optional shortcut that makes the job easier.[!IMPORTANT]- Something they must know to succeed.[!WARNING]- A risk that needs immediate attention.[!CAUTION]- A possible bad outcome of an action.
Alerts sit on top of the spec rather than inside it. Elsewhere they fall back to an ordinary quote, which is still perfectly readable. The blockquotes reference covers the syntax they borrow, including nesting and blank-line rules.
@mentions, issue and commit references
Typing @username links to that person's profile and notifies them. Typing #123 links to issue or pull request 123 in the same repository, and a bare commit SHA links to that commit. These are what make GitHub threads feel wired together. They are also the least portable feature here: in any other renderer they are plain text.
Emoji shortcodes
Type a name between colons and GitHub swaps in the picture. :tada: becomes π, :rocket: becomes π and :bug: becomes π. GitHub supports hundreds of these codes, and Discord and Slack follow the same convention, so the habit transfers.
Footnotes
Footnotes come in two parts: a marker in the sentence and a definition anywhere in the file. Write Claim.[^1] in the text, then a line reading [^1]: Source here. GitHub collects every definition at the bottom of the page and adds back-links for you. Labels can be words instead of numbers, a habit the footnotes reference makes a strong case for.
Mermaid and highlighted code fences
Add a language name after the opening fence - ```python, ```js, ```bash - And the block is colored for that language. Label the fence mermaid instead and GitHub goes further: it renders the contents as a live diagram, version-controlled like any other text. The next section covers both in full.
Syntax highlighting in fenced code blocks
A fenced code block is three backticks, your code, then three backticks again, and everything between the fences is kept verbatim. What turns that grey box into colored code is the language identifier: one short word typed straight after the opening fence, with no space between them.
```python
def greet(name):
print(f"Hello, {name}")
```
```js
const greet = (name) => console.log(`Hello, ${name}`);
```
GitHub picks the colors using Linguist, its open-source language-detection library. It is the same component that draws the language bar on every repository page. Linguist knows several hundred languages along with their common aliases, so py and python reach the same highlighter.
Type an identifier it does not recognize and nothing breaks. No error appears either. The block degrades to a plain monospaced code block, exactly as if you had written no identifier at all. Fences have a handful of other rules worth knowing, and the code blocks reference collects them in one place.
The identifiers you will actually use
| Identifier | Aliases | Use it for |
|---|---|---|
python | py | Python scripts, snippets and doctests |
javascript | js | Browser and Node.js code |
typescript | ts | Typed JavaScript and type-definition files |
bash | sh, shell | Terminal commands and install steps |
json | - | API responses, config files, package manifests |
yaml | yml | CI pipelines, Docker Compose, front matter |
html | - | Markup samples and templates |
css | - | Stylesheets and selector examples |
sql | - | Queries, schema definitions, migrations |
diff | patch | Before-and-after changes, review suggestions |
plaintext | text, txt | Output, logs, anything you want left uncolored |
diff blocks beat describing the change
diff deserves to be learned on its own. Start a line with + for an addition or - for a removal, and GitHub tints the lines green and red:
```diff
- const port = 3000;
+ const port = process.env.PORT || 3000;
```
Turning highlighting off on purpose
At the opposite end sits text, also spelled plaintext. Its whole job is to switch coloring off. Reach for it with terminal output, log excerpts, directory trees and ASCII diagrams. Set a highlighter loose on those and it will invent syntax that is not really there.
Highlighting is a renderer feature, not a Markdown feature
This caveat matters more than any identifier in the table. Neither CommonMark nor the GFM spec says a fence must be colorized. The spec only says the text after the fence is an "info string" that a renderer may use.
In practice that string becomes something like class="language-python" on the <code> element, and a stylesheet or a highlighting library takes it from there. So results differ by tool:
- GitHub and GitLab highlight the block for you.
- A bare CommonMark parser does not, and never claimed it would.
- Static site generators highlight only once you wire up Prism, highlight.js or Chroma.
Our own Markdown editor highlights the block too. It uses highlight.js, loaded only when a document actually contains code. A fence that carries a language tag gets colored; a fence with no tag is left plain on purpose, because a guessed language colors the code wrong.
Tip: Label every fence, even one holding shell output. text tells the next reader the block is meant to stay uncolored, and it stops a future tool from guessing wrong.
Mermaid diagrams from a plain code fence
Mermaid is the most striking thing GitHub does with an info string. Label the fence mermaid and GitHub does not highlight the block at all. It renders the contents as a real diagram:
```mermaid
graph TD;
A[Write Markdown] --> B[Commit];
B --> C[GitHub renders the diagram];
```
Flowcharts, sequence diagrams, class diagrams, state diagrams and Gantt charts all work this way. Each one lives in the repository as plain text and diffs like any other file. GitLab, Notion and Obsidian support Mermaid too. Renderers that do not simply show the source as an ordinary code block.
You can test the block above without leaving this site. Paste it into the Markdown editor and the preview draws the diagram. If the Mermaid source has a typo in it, the fence stays a code block instead of throwing an error at you, so a half-written diagram never breaks the rest of the preview.
Math blocks with $ and $$
GitHub also parses LaTeX-style math. Inline math sits between single dollar signs, as in $E = mc^2$. A display block sits between double dollar signs on their own lines:
$$
\frac{1}{n}\sum_{i=1}^{n} x_i
$$
GitHub renders these with MathJax in READMEs, issues, comments and wikis. A stray dollar sign in ordinary prose can be mistaken for the start of a formula, so escape a literal one as \$. Math depends on the renderer in exactly the way highlighting does. It works on GitHub and in Obsidian, Typora and Pandoc, while a plain CommonMark parser prints the dollar signs as text.
The editor on this site renders math as well, using KaTeX. It accepts $inline$ and $$display$$, plus the \(...\) and \[...\] delimiters that LaTeX users type by habit. So you can check a formula here first, then paste it into a README.
GFM vs CommonMark vs original Markdown
The three names get mixed up constantly, so here is the family tree:
- Original Markdown (2004) is John Gruber's Perl script plus an informal syntax page. Groundbreaking, but vague enough that parsers argued over edge cases for a decade.
- CommonMark (2014) is the standardization effort. It settles every ambiguity with a formal spec and hundreds of test cases, and deliberately adds no new features.
- GFM (spec 2017) is CommonMark plus GitHub's extensions. That is why anything able to parse GFM also parses CommonMark perfectly.
Feature by feature
| Feature | Original (2004) | CommonMark | GFM |
|---|---|---|---|
| Headings, lists, links, emphasis | Yes | Yes | Yes |
| Formal spec + test suite | No | Yes | Yes |
| Tables | No | No | Yes |
| Task lists | No | No | Yes |
| Strikethrough | No | No | Yes |
| Autolinked bare URLs | No | No | Yes |
| Raw-HTML tag filtering | No | No | Yes |
So which one should you write?
Write GFM. It fails gracefully, because a table shown in a CommonMark-only renderer looks like a few odd pipes rather than a broken page. It is also the dialect your teammates, your tools and your future self are most likely to expect.
The exception is a destination you do not control and cannot test. When you are unsure what the far end supports, stay on headings, lists, links and emphasis, which every dialect handles the same way. Our online editor previews full GFM, so you can check a README, an issue comment or a wiki page before you publish it.
Support data verified
Frequently Asked Questions
Is GFM compatible with regular Markdown?
Yes. GFM is defined as a strict superset of CommonMark, which in turn covers all of original Markdown's core syntax. Any standard Markdown document renders correctly under GFM; the reverse isn't always true, because GFM-only features like tables and task lists need a GFM-aware renderer.
Do GFM tables and task lists work outside GitHub?
Mostly, yes. GitLab, Bitbucket, Obsidian, Typora, VS Code's preview, Hugo, and most modern renderers support the core GFM extensions. What usually does not travel: @mentions, #123 issue references, and clickable (interactive) checkboxes - Those depend on GitHub's platform.
How do I add a note or warning box in GFM?
Use alert syntax: a blockquote whose first line is [!NOTE], [!TIP], [!IMPORTANT], [!WARNING] or [!CAUTION]. GitHub renders it as a colored callout with an icon. Note that alerts are a GitHub feature layered on top of the GFM spec, so some other renderers show them as plain blockquotes.
Why doesn't my HTML work in a GitHub README?
GFM filters a specific list of dangerous tags - <script>, <style>, <iframe>, <textarea> and a few others - For security. Harmless tags like <details>, <sub> and <br> still work. If layout HTML seems ignored, also check that it isn't inside a code fence.
Where can I test GFM syntax before publishing?
Our free online Markdown editor renders GFM live as you type - Tables, task lists, strikethrough, code fences and more - So you can verify a README or issue comment before pasting it into GitHub. Nothing you type leaves your browser.
What is CommonMark?
CommonMark is the formal specification that finally pinned down what Markdown means. Gruber's 2004 syntax page was written as prose, so two parsers could read the same file two different ways; the 2014 spec replaced that with exact rules and hundreds of test cases, and deliberately added no new features. GFM is defined as a strict superset of it, which is why any valid CommonMark file renders identically on GitHub. The what is Markdown page tells the rest of that story.
Can I use emoji in Markdown?
Yes. Paste the Unicode character straight into the file and it shows up everywhere, because to Markdown it is ordinary text like any other letter. The :shortcode: style, such as :tada: for a party popper, is a different thing: it is a platform feature, not Markdown syntax. GitHub, GitLab, Discord and Slack swap the code for a picture, while plain CommonMark leaves the colons sitting on screen.
What is Mermaid in Markdown?
Mermaid lets you describe a diagram in plain text inside a fenced block tagged mermaid, and the renderer draws the picture for you. GitHub and GitLab render those blocks as real flowcharts, sequence diagrams and Gantt charts, so the diagram lives in the repository as text and diffs like any other file. Most other renderers show the raw Mermaid source instead, because this is a renderer feature rather than Markdown syntax; the fence itself is an ordinary code block with a label on it.
Keep learning
Try the Editor
Try the Editor