Markdown is everywhere: README files, GitHub issues, documentation sites, Slack messages, blog posts, and even this article. Created by John Gruber in 2004, Markdown is a lightweight markup language that converts plain text into formatted HTML. If you write code, you write Markdown — so you should write it well.

Markdown's genius is that a well-written document is readable in two forms at once: as rendered HTML and as the raw plain text itself. That's why it won — no proprietary format, no special editor required, and it diffs cleanly in version control, which makes documentation reviewable in pull requests just like code. One caveat before we start: "Markdown" is really a family of dialects. The examples in this guide follow CommonMark plus the GitHub Flavored Markdown (GFM) extensions, which is the combination you'll meet on GitHub, GitLab, and most documentation generators.

Basic Syntax

Headings

Hash marks map directly to HTML heading levels: one # becomes <h1>, two become <h2>, and so on. Treat headings as a document outline, not a font-size picker — use exactly one H1 for the title, H2 for major sections, and don't skip levels, since screen readers and generated tables of contents rely on the hierarchy. A detail that trips people up: the space after the hashes is required by CommonMark, so ##Heading renders as literal text on GitHub.

Markdown Headings
# Heading 1  (H1 — use once per document)
## Heading 2  (H2 — major sections)
### Heading 3  (H3 — sub-sections)
#### Heading 4  (H4 — rarely needed)

Text Formatting

Asterisks handle emphasis: single for italic, double for bold, triple for both. Underscores work identically (_italic_, __bold__), but pick one style and stay consistent — asterisks are the safer default because underscores inside words (like variable_names) can be misinterpreted by some renderers. The backtick for inline code is the one developers should use most and often forget: wrapping file names, commands, and identifiers in backticks prevents the renderer from mangling them and visually separates code from prose.

Bold, Italic, Strikethrough
**bold text**
*italic text*
***bold and italic***
~~strikethrough~~
`inline code`

Lists

Unordered lists take -, *, or + as bullets (again: pick one, - is conventional), and nesting is a matter of indentation — two spaces is enough for simple bullets. A genuinely useful trick for ordered lists: you can number every item 1. and the renderer will count for you, which means inserting an item mid-list never forces you to renumber everything below it. Task lists (- [ ] / - [x]) are a GFM extension, and on GitHub issues and pull requests they become live checkboxes that update a visible progress bar.

Ordered & Unordered Lists
- Unordered item 1
- Unordered item 2
  - Nested item (2 spaces)
  - Another nested item

1. Ordered item 1
2. Ordered item 2
3. Ordered item 3

- [x] Completed task (GitHub)
- [ ] Incomplete task

Links & Images

The mnemonic for link syntax: square brackets hold what the reader sees, parentheses hold where it goes — and an image is just a link with an exclamation mark in front, where the bracketed text becomes the alt text. Write alt text that describes the image's content, not "screenshot" or "image". Reference-style links, shown at the bottom of the example, move the URLs out of the sentence entirely; in a document that links to the same docs page five times, this keeps the prose readable in source form and gives you exactly one place to update the URL.

Links & Images
[Link text](https://example.com)
[Link with title](https://example.com "Hover title")
![Alt text for image](https://example.com/image.png)


[Click here][docs]
[Another link][docs]

[docs]: https://docs.example.com

Code Blocks

Fenced code blocks — three backticks on their own lines — are where Markdown earns its keep for developers. The word after the opening fence is the language identifier, and it's what activates syntax highlighting on GitHub and virtually every documentation site; a block without one renders as flat monochrome text. Use accurate identifiers (bash for shell commands, json, diff for patches — which colors added and removed lines green and red). If you ever need to show a code block inside a code block, use four backticks for the outer fence.

Code Blocks
Inline code: `const x = 42;`

Fenced code block with syntax highlighting:
```javascript
function greet(name) {
  return `Hello, ${name}!`;
}
```

```bash
npm install express
npm run dev
```

Tables

Tables are a GFM extension built from pipes and a mandatory separator row of dashes. Colons in that separator row control alignment: :--- left, :---: center, ---: right — right-alignment is the professional touch for numeric columns. You don't have to line up the pipes vertically in source (renderers don't care), but doing so keeps the raw text readable, and most editors can reformat tables for you. Know the limits, too: Markdown tables can't span cells or hold multi-paragraph content, so when a table gets that complex, either restructure the information or drop into raw HTML, which GFM allows inline.

Markdown Tables
| Feature    | Chrome | Firefox | Safari |
|------------|:------:|:-------:|:------:|
| WebP       | ✅     | ✅      | ✅     |
| AVIF       | ✅     | ✅      | ✅     |
| JPEG XL    | ❌     | ❌      | ✅     |

Alignment:
| Left  | Center | Right |
|:------|:------:|------:|
| Left  | Center | Right |

Blockquotes

A leading > renders the line as a quotation. Beyond literal quotes, developers use blockquotes for callouts — notes, warnings, asides — and GFM formalizes this with the alert syntax covered below. Blockquotes nest (>>) and can contain any other Markdown, including lists and code blocks, which makes them handy for quoting a bug report complete with its reproduction steps.

Blockquotes
> This is a blockquote.
> It can span multiple lines.
>
> — Author Name

GitHub Flavored Markdown (GFM)

GitHub extends standard Markdown with useful additions:

  • Task lists: - [x] Done / - [ ] Todo
  • Auto-linked URLs: Just paste a URL and it becomes clickable
  • Emoji: :rocket: → 🚀, :bug: → 🐛
  • Alerts: > [!NOTE], > [!WARNING], > [!CAUTION]
  • Syntax highlighting: Fenced code blocks with language identifiers
  • Footnotes: Text[^1] and [^1]: Footnote content

Of these, alerts are the most underused. Wrapping a line in > [!WARNING] renders it as a colored callout box with an icon, which is dramatically more visible than bold text for things like "this command deletes data." Just remember that GFM extensions are not universal — alerts and task lists may render as plain blockquotes and literal brackets on other platforms, so check what your target renderer supports before leaning on them in documentation that travels.

Writing a Great README

The README is usually the first — and often the only — documentation a visitor reads, and its job is to answer three questions fast: what is this, how do I install it, and how do I use it. The template below orders sections by urgency for exactly that reason: description first, installation second, a copy-pasteable usage example third, and reference material after. The single highest-impact rule is that the usage example must actually work when pasted; a README whose first code sample throws an error loses the reader's trust immediately, so re-verify examples whenever the API changes.

README Template
# Project Name

Brief description of what this project does.

## Installation

```bash
npm install my-package
```

## Usage

```javascript
import { thing } from 'my-package';
thing.doSomething();
```

## API Reference

### `doSomething(options)`

| Parameter | Type   | Default | Description       |
|-----------|--------|---------|-------------------|
| `timeout` | number | 5000    | Timeout in ms     |
| `retries` | number | 3       | Number of retries |

## Contributing

1. Fork the repo
2. Create your branch (`git checkout -b feature/amazing`)
3. Commit your changes
4. Push and open a Pull Request

## License

MIT

Best Practices

  • One H1 per document — use H2 and H3 for sections
  • Leave blank lines before and after headings, lists, and code blocks
  • Use reference-style links for long or repeated URLs
  • Add language identifiers to fenced code blocks for syntax highlighting
  • Keep lines under 80-100 characters for readability in plain text
  • Use alt text for images — not just for accessibility, also for when images fail to load

The blank-line rule deserves emphasis because it causes the most confusing bugs: a list that starts immediately after a paragraph without a blank line may render as part of that paragraph, and a code fence glued to the text above it can fail to open at all. When Markdown "isn't working," missing blank lines are the first thing to check. A linter such as markdownlint (available as a VS Code extension and a CI tool) catches these issues plus inconsistent bullets and heading skips automatically, and is worth adding to any repository where documentation matters.

Next Steps

The fastest way to internalize Markdown is to write your next README in it deliberately: structure the headings as an outline first, add language identifiers to every code fence, and run the preview side by side (Ctrl+Shift+V in VS Code) as you go. From there, the same skills scale up — static site generators like MkDocs, Docusaurus, and Hugo turn folders of Markdown files into full documentation sites, and GitHub renders any .md file in a repository automatically. Because the format is plain text, everything you write stays greppable, diffable, and portable across whatever tool comes next — which is precisely why Markdown, two decades on, is still the default answer for developer writing.

Try Our Free Developer Tools

Format and encode your documentation content.