Coding

Markdown From the Ground Up — Syntax, Flavours, and When to Use It

Markdown is everywhere. GitHub READMEs, technical documentation, blog posts, Slack messages, Notion pages, Discord — all of them speak Markdown. Yet most people pick it up piecemeal: they learn # for headings, ** for bold, and call it a day. This article starts from the beginning and covers everything: what Markdown actually is, the full syntax, the different flavours, how it converts to HTML, and when to reach for something else.

What Markdown Actually Is

Markdown is a text-to-HTML conversion tool invented by John Gruber and Aaron Swartz in 2004. The core idea was simple: write plain text that looks good as plain text, and have a program convert it to clean HTML. A pound sign before a word becomes a heading tag. Asterisks around text become a bold tag. Dashes at the start of lines become a list.

The design goal was readability. Markdown source should be publishable as-is, as plain text, without looking like it's been marked up with tags. That's what separates it from HTML — an HTML document is not pleasant to read in raw form. A Markdown document is.

"The overriding design goal for Markdown's formatting syntax is to make it as readable as possible." — John Gruber

Markdown is not a programming language, a binary format, or a markup language in the XML sense. It's just a convention — an agreed set of plain-text patterns that tools know how to convert. This simplicity is its greatest strength and, occasionally, its biggest frustration.

How the Conversion Works

When you write Markdown, a parser reads your plain text and emits HTML. The original Gruber parser was a Perl script. Today there are implementations in every language: marked.js and micromark in JavaScript, Python-Markdown and mistune in Python, pulldown-cmark in Rust, and dozens more.

The mapping is direct. Here's what happens to common syntax:

Markdown
# My Heading Some **bold** and *italic* text. - Item one - Item two [Link](https://example.com)
Rendered HTML
<h1>My Heading</h1> <p>Some <strong>bold</strong> and <em>italic</em> text.</p> <ul> <li>Item one</li> <li>Item two</li> </ul> <a href="https://example.com">Link</a>

The important thing to understand: Markdown is HTML at the end. It's not a replacement — it's a convenient way to write HTML without typing angle brackets. This means anything you can do in HTML, you can technically do in most Markdown parsers by just writing raw HTML inline. But you rarely need to.

The Complete Syntax

Headings

Use # signs followed by a space. One # gives you an <h1>, two give <h2>, and so on up to six levels.

# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6

There's also setext style for H1 and H2 — underline the text with === or ---:

My H1 Heading
=============

My H2 Heading
-------------

Paragraphs and Line Breaks

Paragraphs are created by separating text with a blank line. A single line break within a paragraph is ignored — the text wraps together. To force a line break within a paragraph, end the line with two or more spaces before pressing Enter.

⚠️

The two-space line break is invisible. This trips up a lot of writers — editors that strip trailing whitespace will silently remove your intended line breaks. Some flavours accept a backslash \ at end of line as a more visible alternative.

Emphasis

**bold text**          → <strong>
*italic text*          → <em>
***bold and italic***  → <strong><em>
~~strikethrough~~      → <del>  (GFM only)
__bold with underscores__
_italic with underscore_

Lists

Unordered lists use -, *, or + as the bullet marker. Ordered lists use numbers followed by a period. The actual numbers don't have to be sequential — most parsers renumber them anyway.

- Unordered item
- Another item
  - Nested item (indent 2 spaces)

1. First
2. Second
3. Third

1. The numbers
1. don't need to
1. be sequential

Task Lists (GFM)

GitHub Flavored Markdown adds task list items — checkboxes that can be checked or unchecked:

- [x] Write the article
- [x] Add code examples
- [ ] Add images
- [ ] Publish

Code

Inline code uses single backticks. Fenced code blocks use triple backticks with an optional language name for syntax highlighting:

Use `npm install` to install dependencies.

```javascript
const greet = name => `Hello, ${name}!`;
console.log(greet('World'));
```

```python
def greet(name):
    return f"Hello, {name}!"
```

You can also indent code with 4 spaces, but fenced blocks are strongly preferred because they support language labels.

Links and Images

[Link text](https://example.com)
[Link with title](https://example.com "Hover text")
![Image alt text](https://example.com/image.png)
![Image with title](image.png "Image title")

[Reference-style link][ref-name]
[ref-name]: https://example.com

Blockquotes

Prefix lines with >. Blockquotes can contain other Markdown elements, and they nest with multiple > signs:

> This is a blockquote.
> It can span multiple lines.
>
>> This is nested.

> **Note:** blockquotes can contain **bold** text.

Horizontal Rules

Three or more hyphens, asterisks, or underscores on a line by themselves:

---
***
___

Tables (GFM)

Tables are a GitHub Flavored Markdown extension. They use pipe characters to separate columns and a separator row of dashes between header and body:

| Name     | Age | Role       |
|----------|-----|------------|
| Alice    | 30  | Engineer   |
| Bob      | 25  | Designer   |
| Charlie  | 35  | Manager    |

| Left     | Center | Right    |
|:---------|:------:|---------:|
| aligned  | middle | aligned  |

Escaping Special Characters

To display a character that Markdown would normally interpret as syntax, prefix it with a backslash:

\*not italic\*
\# not a heading
\[not a link\]
\`not code\`

The Flavour Problem

Gruber's original Markdown spec left many edge cases undefined. What happens when you nest bold inside italic? What does a list item followed immediately by a heading look like? Different parsers answered these questions differently, and the ecosystem fragmented into dozens of incompatible "Markdown flavours."

FlavourTablesTask listsStrikethroughFootnotesMathUsed by
Original MarkdownDaring Fireball
CommonMarkReddit, Stack Overflow
GFMGitHub, GitLab
MultiMarkdown~Academic writing
Pandoc MarkdownDocument conversion
MDX~~React/Next.js docs

CommonMark — the attempt at a standard

In 2014, a group including Jeff Atwood (Stack Overflow) and John MacFarlane worked to create CommonMark — a precise, unambiguous Markdown specification. Every edge case is defined. The spec has a test suite. It's the closest thing Markdown has to a standard.

CommonMark doesn't include tables, task lists, or strikethrough — those are GFM extensions that most implementations add on top.

GitHub Flavored Markdown (GFM)

GFM is the Markdown dialect most developers encounter. It's a CommonMark superset that adds:

  • Tables — pipe-separated with separator row
  • Task lists- [x] checkboxes
  • Strikethrough~~text~~
  • Autolinks — bare URLs become clickable links
  • Disallowed HTML — certain HTML tags are filtered for security

If you're writing for GitHub, GitLab, or most documentation tools, GFM is what you're using.

MDX

MDX is Markdown for the React ecosystem. It lets you use JSX components directly inside Markdown files — essentially writing Markdown that can render interactive React components. It's used by documentation frameworks like Docusaurus, Nextra, and Astro.

## My Post

Here's a normal paragraph.

<MyComponent prop="value" />

And back to normal **Markdown**.

Front Matter

Many static site generators extend Markdown files with YAML front matter — a block of metadata at the top of the file, fenced with triple dashes. It's not standard Markdown, but it's nearly universal in the documentation and blogging tool space:

---
title: My Blog Post
date: 2026-06-06
author: Jane Smith
tags: [markdown, writing, web]
published: true
---

# My Blog Post

The body starts here...

Tools like Jekyll, Hugo, Eleventy, and Astro read this metadata to build page titles, dates, category pages, and more.

Writing Better Markdown

Use blank lines liberally

The most common Markdown mistake is not having enough blank lines. A heading not preceded by a blank line, a list not separated from the paragraph before it, a code block with no breathing room — all of these can cause parsers to misinterpret your structure. When in doubt, add a blank line.

Pick one bullet style and stick to it

You can use -, *, or + for unordered lists. They all work, but mixing them in the same file looks inconsistent. Most style guides prefer -.

Use fenced code blocks, not indented ones

Four-space indented code blocks are original Markdown, but fenced blocks (```) are clearer, support language labels, and work everywhere. Always prefer fenced blocks.

Don't use Markdown for complex layouts

Markdown was designed for prose. It handles headings, paragraphs, lists, links, and code beautifully. It doesn't handle multi-column layouts, sidebars, custom components, or complex nesting well. If you find yourself fighting the parser, you've outgrown what Markdown is for.

💡

Use a linter. Tools like markdownlint enforce consistent style — heading hierarchy, blank lines around blocks, consistent list markers, no trailing whitespace. Running it in CI prevents style drift across a team documentation repo.

Markdown vs. Raw HTML

Most Markdown parsers allow raw HTML inline. You can write <div class="warning">...</div> inside a Markdown file and it'll render. But you generally shouldn't:

  • It ties your content to a specific HTML structure
  • It makes the source harder to read — defeating Markdown's purpose
  • Many parsers sanitise or block raw HTML for security (GitHub does)
  • It won't work with all Markdown targets (some output PDF, EPUB, LaTeX, not HTML)

Reserve raw HTML for cases where Markdown genuinely can't express what you need — like a <video> embed or a custom element with specific attributes.

When to Use Something Else

NeedBetter choice than plain Markdown
Rich components, interactive elementsMDX (React/Vue) or AsciiDoc
Academic writing with citations, mathPandoc Markdown or LaTeX
Complex document structureAsciiDoc or reStructuredText
Multi-output formats (PDF, EPUB, HTML)Pandoc
Collaborative editing with commentsGoogle Docs, Notion, Confluence
CMS-managed content with rich mediaMDX, Contentful, Sanity

For most technical writing, documentation, README files, and blog posts, plain Markdown — or GFM — is the right tool. It's simple, portable, version-controllable, and readable in any text editor. Use it until you hit a wall, then consider the alternatives.

Write and preview Markdown right now
Our free Markdown editor shows live HTML preview as you type. Export to .md or a styled .html file. Supports GFM tables, task lists, code blocks, and the full syntax covered in this article.
Open Markdown Editor →

Where Markdown Shows Up

Once you know to look, Markdown is everywhere:

  • GitHub / GitLab — READMEs, issues, pull request descriptions, wiki pages
  • Stack Overflow — all question and answer formatting
  • Slack / Discord / Teams — message formatting (simplified subset)
  • Notion / Obsidian / Bear — note-taking apps
  • Jekyll / Hugo / Eleventy / Astro — static site generators
  • npm / PyPI / Cargo — package documentation
  • Jupyter Notebooks — text cells between code cells
  • VS Code — built-in preview, README rendering in the file explorer
  • Jira / Linear / GitHub Projects — issue descriptions

Quick Reference

# H1   ## H2   ### H3 … ###### H6

**bold**   *italic*   ***both***   ~~strike~~   `code`

- unordered    1. ordered    - [x] task    - [ ] task

[text](url)    ![alt](url)

> blockquote

---  (horizontal rule)

| Col | Col |         ```js
|-----|-----|         code block
| a   | b   |         ```
📝
The Tool Empire Team
We build free browser-based tools and write about the practical techniques behind them. No signup required, nothing sent to a server.

Related Tools

Related Articles