Markdown Syntax Guide: The Complete Reference for 2026

10 min3 Haziran 2026

What Markdown Is (and Why Developers Adopted It)

Markdown was created by John Gruber in 2004, with significant input from Aaron Swartz on the syntax. The goal was simple: a plain-text format that reads naturally without rendering but can convert cleanly to HTML. Gruber's original Perl script was the first parser, and the syntax was deliberately minimal — it borrowed conventions people already used in plain-text emails (asterisks for emphasis, blank lines for paragraphs, indentation for code).

The reason developers adopted Markdown over alternatives like reStructuredText or Textile comes down to one thing: it gets out of your way. A Markdown file is readable without any special tool. You can open it in Notepad, cat it in a terminal, or read it in a git diff. That plain-text-first philosophy meant it worked everywhere — README files, commit messages, inline documentation, wikis. When GitHub made Markdown the default format for READMEs and issues in 2009, adoption exploded.

Today Markdown is the default writing format for GitHub, GitLab, Stack Overflow, Reddit, Discord, Notion, Obsidian, Hugo, Jekyll, Docusaurus, and hundreds of other platforms. It's the lingua franca of developer documentation. But Gruber's original spec was intentionally vague on edge cases — what happens with nested blockquotes, or how lazy continuation lines work. That ambiguity led to dozens of slightly incompatible parsers.

In 2014, John MacFarlane (creator of Pandoc) and a group of Markdown implementors launched CommonMark — a strict specification with over 600 test cases that nails down every ambiguous corner. Most modern parsers (markdown-it, cmark, pulldown-cmark, goldmark) implement CommonMark as their baseline. GitHub Flavored Markdown (GFM) extends CommonMark with tables, task lists, autolinks, and strikethrough. When someone says "Markdown" today, they usually mean CommonMark plus GFM extensions.

Basic Formatting: Headings, Bold, Italic, Strikethrough

Headings use the # prefix syntax. One # for h1, two ## for h2, up to six ###### for h6. Always put a space after the hash marks — #Heading won't parse correctly in most renderers. You can also use the "setext" style (underline with === for h1 or --- for h2) but almost nobody does anymore because it only supports two levels and is harder to scan in source. Stick with ATX-style # headings.

Bold text is wrapped in double asterisks **like this** or double underscores __like this__. Italic uses single asterisks *like this* or single underscores _like this_. You can combine them: ***bold and italic*** or ___bold and italic___. The convention in most projects is to use asterisks for both, because underscores have a gotcha — they don't work inside words. Write foo_bar_baz and most parsers treat it as a literal underscore, not italic. Asterisks always work: foo*bar*baz renders with "bar" italic.

Strikethrough uses double tildes: ~~deleted text~~. This is a GFM extension, not part of CommonMark core, but it's supported virtually everywhere developers write Markdown. Line breaks are trickier than they should be. A single newline in your source is treated as a space in the output (lines flow together into a paragraph). To force a line break without starting a new paragraph, end a line with two trailing spaces or use a <br> tag. Trailing spaces are invisible and git hooks often strip them, so <br> is the more reliable option.

Horizontal rules are three or more hyphens (---), asterisks (***), or underscores (___) on their own line. I recommend --- because it's the most common convention. Watch out for the ambiguity with setext headings: if you have text on the line directly above ---, the parser treats it as an h2 heading, not a horizontal rule. Add a blank line above your --- to avoid this.

Links, Images, and Reference-Style Syntax

Inline links follow the pattern [link text](URL). You can add a title attribute in quotes: [Google](https://google.com "Search engine"). The title shows as a tooltip on hover. For links within the same repository, use relative paths: [see the docs](./docs/README.md). Relative links survive forks and branch changes better than absolute GitHub URLs.

Images use the same syntax with an exclamation mark prefix: ![alt text](image-url.png). The alt text is critical for accessibility — screen readers use it to describe the image. Don't leave it empty. Write descriptive alt text: ![Diagram showing the request-response cycle between client and server](./images/request-flow.png). You can also link an image by wrapping the image syntax in link syntax: [![alt](img.png)](https://example.com).

Reference-style links separate the URL from the text, which cleans up long paragraphs. Define references anywhere in the document: [1]: https://example.com "Title". Then use them inline: [click here][1] or even just [example.com][1]. You can also use the link text as the reference key: [CommonMark] links to whatever you defined as [CommonMark]: https://commonmark.org. Reference links make it easy to reuse the same URL multiple times and keep your source text readable.

A common mistake with URLs: spaces must be percent-encoded (%20) or the URL wrapped in angle brackets. [link](<path with spaces/file.md>) handles spaces correctly. For email links, use angle brackets: <[email protected]> becomes a clickable mailto link automatically. Bare URLs only auto-link in GFM (GitHub Flavored Markdown), not in base CommonMark — if you need compatibility, always wrap URLs in angle brackets or use explicit link syntax.

Code Blocks and Syntax Highlighting

Inline code uses single backticks: `const x = 5`. If your code contains backticks, use double backticks as the delimiter: ``use a `backtick` inside code``. The opening and closing delimiters just need to match in count. You can even use triple backticks for inline code containing double backticks, though at that point you probably want a code block instead.

Fenced code blocks use triple backticks (```) or triple tildes (~~~) on their own line, with the code between them. Add a language identifier after the opening fence for syntax highlighting: ```javascript or ```python. Most renderers support hundreds of language identifiers — common ones include js, ts, py, rb, go, rust, bash, sql, json, yaml, html, css. The identifier is case-insensitive in most parsers.

Indented code blocks are the older syntax: indent every line by 4 spaces or 1 tab. They're still valid CommonMark but have no way to specify a language for highlighting. They also interact badly with list items (which already use indentation for nesting). Fenced blocks are strictly better — use them exclusively. The only time you'll encounter indented blocks is in older documentation.

One practical tip: when documenting Markdown itself (showing Markdown syntax in a Markdown file), you need to escape the fences. Either use more backticks for the outer fence (four backticks ```` wrapping content that contains triple backticks) or use tildes for one and backticks for the other. This nesting comes up constantly when writing documentation about documentation.

# Fenced code block with language
```typescript
interface User {
  id: string;
  name: string;
  email: string;
}

function greet(user: User): string {
  return `Hello, ${user.name}!`;
}
```

# Inline code
Use `npm install` to add dependencies.

# Escaping: showing triple backticks inside a code block
````markdown
```javascript
console.log("nested fence");
```
````

Lists, Tables, and Task Lists

Unordered lists use -, *, or + as bullet markers. Pick one and be consistent — mixing them in the same list is valid but confusing. Ordered lists use numbers followed by a period: 1. First item. The actual numbers don't matter for rendering (1. 1. 1. produces 1, 2, 3 in output) but using sequential numbers makes the source more readable. Nesting requires indentation — the nested item must align with the text content of the parent item, which typically means 2-4 spaces depending on the marker width.

Tables use pipes and hyphens. The header row, separator row, and data rows form a grid. Alignment is set in the separator row: :--- for left, :---: for center, ---: for right. Cells don't need to be perfectly aligned in the source (most formatters fix this for you) but the pipe characters must be present. Tables cannot contain block-level content (no paragraphs, code blocks, or lists inside cells) — only inline formatting works.

Task lists (GFM extension) turn list items into checkboxes. Use - [ ] for unchecked and - [x] for checked items. These render as interactive checkboxes on GitHub issues and pull requests — clicking them updates the Markdown source. Task lists nest inside regular lists and work well for tracking progress in issue descriptions or project plans.

A major compatibility pitfall with lists: the required indentation for nesting differs between parsers. CommonMark requires that continuation content aligns with the first non-space content of the list item (so 3 spaces for "- " items, 4 for "1. " items). Some older parsers accept 2 spaces for everything. If your nested lists render flat instead of nested, check your indentation against CommonMark rules.

## Table with alignment

| Feature       | CommonMark | GFM  | MDX  |
| :------------ | :--------: | :--: | ---: |
| Tables        |     No     | Yes  |  Yes |
| Task lists    |     No     | Yes  |  Yes |
| Strikethrough |     No     | Yes  |  Yes |
| JSX           |     No     |  No  |  Yes |

## Task list

- [x] Write introduction
- [x] Add code examples
- [ ] Review for accuracy
- [ ] Publish

## Nested list

1. Backend setup
   - Install dependencies
   - Configure database
     - Run migrations
     - Seed test data
2. Frontend setup
   - Create component scaffolding
   - Wire up API calls

GitHub Flavored Markdown (GFM) Extensions

GFM is a superset of CommonMark maintained by GitHub. It adds features that developers need constantly: tables, task lists, strikethrough, autolinks, and disallowed raw HTML tags (for security in user-generated content). The GFM spec is publicly documented and implemented by several parsers beyond GitHub itself, including markdown-it (with plugins) and remark-gfm.

Autolinks turn bare URLs into clickable links without requiring explicit [text](url) syntax. Type https://example.com in a GFM document and it renders as a link. This also works for email addresses. The autolinking is smart enough to handle trailing punctuation — a URL followed by a period won't include the period in the link. Extended autolinks (a GFM-specific extension beyond CommonMark autolinks) also recognize www. prefixes without the protocol.

Emoji shortcodes like :rocket: and :white_check_mark: render as emoji on GitHub. This is not part of the GFM spec proper — it's a GitHub-specific rendering feature. The shortcodes won't work in most Markdown renderers outside GitHub. If you need emoji that works everywhere, paste the actual Unicode character (🚀, ✅) instead of using shortcodes. GitHub also supports alerts/admonitions with a special blockquote syntax: > [!NOTE], > [!WARNING], > [!CAUTION] create colored callout boxes. These were added in late 2023 and are now widely copied by other platforms.

One thing to watch: GFM disables certain raw HTML tags for security reasons when rendering user-generated content (issues, comments, wiki). Tags like <script>, <style>, <iframe>, and event handler attributes are stripped. But in repository files (README.md, docs), most HTML is preserved. This asymmetry confuses people — HTML that works in your README won't work in an issue comment. Write your Markdown to work without raw HTML when possible.

Advanced: Footnotes, Math (KaTeX/MathJax), Diagrams (Mermaid)

Footnotes let you add references without cluttering the main text. The syntax is [^1] in the body and [^1]: footnote text at the bottom. Footnote identifiers can be numeric or named: [^note-about-performance]. The rendered output places footnotes at the bottom of the document with backlinks. Footnotes are not part of CommonMark or GFM, but they're supported by GitHub (since 2021), Obsidian, markdown-it (with plugin), and most static site generators.

Math rendering uses LaTeX syntax. Inline math is wrapped in single dollar signs: $E = mc^2$ renders as a formatted equation. Block math uses double dollar signs on their own lines: $$\sum_{i=1}^n x_i$$ renders as a centered equation block. GitHub added native math support (using KaTeX) in 2022. Other platforms may use MathJax instead — the LaTeX syntax is the same, but rendering details differ slightly (KaTeX is faster but supports fewer commands than full MathJax).

Mermaid diagrams render flowcharts, sequence diagrams, Gantt charts, and more from text descriptions in a code block tagged with the mermaid language. GitHub renders Mermaid natively in Markdown files. Write ```mermaid followed by graph TD; A-->B; and you get a rendered flowchart. Other platforms (GitLab, Notion, Docusaurus) also support Mermaid, though version support varies — new Mermaid features might not render everywhere immediately.

The key limitation of all these advanced features: they require specific renderer support. A plain Markdown-to-HTML converter won't render math, Mermaid, or footnotes unless configured with the right plugins. Before using these features, check what your target platform supports. If you're writing docs that might be read on multiple platforms (npm README, GitHub, your blog), stick to features that work everywhere and use images as fallbacks for diagrams.

Common Mistakes and Compatibility Issues

The number one source of rendering bugs is trailing spaces and invisible characters. Markdown uses two trailing spaces to signal a hard line break, but most editors strip trailing whitespace (and they should — it's noise in diffs). Use <br> for intentional line breaks instead. Also watch for non-breaking spaces (U+00A0) that look like regular spaces but break parsing — they commonly appear when copying text from Word or web pages.

Nested list indentation trips up everyone. CommonMark says the content after a list marker establishes the indentation level for nested items. For "- " (dash space) that's 2 characters, so nested content needs 2+ spaces. For "1. " (number dot space) that's 3 characters, so you need 3+ spaces. But many people use 4 spaces for everything because older parsers required it. The safe bet: use 4 spaces for nesting under any list marker. It works in both old and new parsers.

Raw HTML in Markdown is valid but creates portability problems. A <details><summary>Click to expand</summary> block works on GitHub but might be stripped or rendered as text in other contexts. MDX (Markdown + JSX) takes a different approach entirely — it lets you import React components into Markdown but breaks standard Markdown compatibility. If your content needs to work across platforms, minimize raw HTML and use only well-supported Markdown syntax.

Renderer differences still matter in 2026. CommonMark, GFM, MDX, Pandoc Markdown, and PHP Markdown Extra all parse edge cases differently. A blank line inside a list item, a blockquote followed by a paragraph, or a heading inside a blockquote can produce different output depending on the parser. The practical solution: test your Markdown in the renderer your audience will use, avoid edge cases when possible, and use a linter like markdownlint to catch common issues before they reach production.