Markdown Cheat Sheet: CommonMark and GitHub Flavored

2026-03-27 · SPUNK13 · spunk.bet

Markdown is not one language. CommonMark is the strict specification; GitHub Flavored Markdown adds tables, task lists, strikethrough and autolinks on top; and every static site generator bolts on a few more. The syntax below notes which layer each feature belongs to, because that is where the surprises are.

The Core (CommonMark, Works Everywhere)

# H1   ## H2   ### H3        (space after the hashes is required)

*italic*  **bold**  `code`  ***both***

> blockquote
>> nested

- bullet
  - nested needs 2 spaces
1. ordered
1. every item can be "1." - renderers renumber

[link](https://example.com "optional title")
![alt text](image.png)

---            horizontal rule (3+ hyphens, asterisks or underscores)

Line Breaks: The Most Common Bug

A single newline is not a line break. Markdown joins consecutive lines into one paragraph. To force a break you either end the line with two trailing spaces (invisible, and stripped by many editors on save) or use a backslash at end of line, which CommonMark supports and is far more maintainable. A blank line starts a new paragraph and is almost always what you actually wanted.

Fenced Code and Language Hints

```python
def hello(): ...
```

~~~
use tildes when the block itself contains backticks
~~~

Inline code with a backtick: ``a ` b``

Indented code blocks (four spaces) still work but break inside list items in confusing ways. Use fences. The language tag drives syntax highlighting and is worth adding even when the renderer ignores it, because linters and diff tools read it.

Tables (GFM, Not CommonMark)

| Tool  | Speed | Notes        |
|:------|:-----:|-------------:|
| ripgrep | fast | left/center/right |

The colons in the separator row set alignment. Column widths in the source do not matter — only the pipes do. Tables cannot contain block elements, so a multi-paragraph cell or a nested list is impossible; use raw HTML or restructure. Escape a literal pipe inside a cell as \|.

Task Lists, Strikethrough, Footnotes

- [x] done
- [ ] not done

~~struck through~~

Here is a claim.[^1]

[^1]: And the footnote text, defined anywhere in the document.

Task lists and strikethrough are GFM. Footnotes are not in GFM's original spec but are supported by GitHub, Pandoc, MkDocs and most modern generators — check yours before relying on them.

Escaping

Backslash-escape any of \ ` * _ { } [ ] ( ) # + - . ! to render it literally. The one that bites people is underscores inside identifiers: my_var_name renders as italic in older parsers because of the mid-word underscore. CommonMark fixed this for intraword underscores, but plenty of parsers in the wild have not. Wrapping identifiers in backticks solves it permanently and is better typography anyway.

Reference Links for Readable Source

See the [specification][spec] and the [reference parser][ref].

[spec]: https://spec.commonmark.org/
[ref]: https://commonmark.org/dingus/

Definitions can sit at the bottom of the file and are not rendered. For documents with many links this keeps paragraphs readable and makes updating a URL a one-line change.

Front Matter

Most static site generators expect a YAML block delimited by --- as the very first thing in the file — no blank line, no BOM before it. Getting a byte-order mark from a Windows editor is a classic cause of "my front matter is showing up as body text". Save as UTF-8 without BOM.

Linting

Run markdownlint (or markdownlint-cli2) in CI. It catches missing blank lines around fences and lists, inconsistent heading levels, and trailing spaces — the exact class of issue that renders fine in one parser and wrongly in another. Add a .markdownlint.json disabling the rules you disagree with rather than skipping the tool.

Explore More

Free tools, guides, and resources.

Visit spunk.bet
400+ ToolsCasinoMemesAstrologyScam DB