Developers & Technical · Guide · Developer Utilities
How to write clean markdown
Format clean and compatible Markdown across different flavors. Learn syntax rules and lint your files for free, instantly, with our online guide — no download or sign-up required.
Markdown is the lingua franca of developer writing: READMEs, commit bodies, GitHub issues, docs sites, Obsidian notes, Notion pages, Substack, chat apps. It’s easy to write and hard to write well. Clean markdown renders consistently across platforms, diffs cleanly in git, and stays maintainable as it grows. Messy markdown breaks renderers, hides bugs in nested lists, and turns documents into git-diff nightmares. This guide covers the syntax (including the edge cases), flavor differences (CommonMark vs GFM vs others), style conventions, linting tools, and the patterns that separate readable markdown from the kind that works on your machine and nobody else’s.
Advertisement
Markdown flavors — what actually works
CommonMark: the standardized spec. Defines exactly what the core syntax means. Reference implementation for “portable” markdown.
GitHub Flavored Markdown (GFM): CommonMark + tables, strikethrough, task lists, autolinks, fenced code with language hints. What most developers actually write. Supported by GitHub, GitLab, many docs generators.
MDX: markdown + JSX components. Used by Next.js docs, Docusaurus, many modern docs sites. Breaks portability — MDX files don’t render on GitHub.
Pandoc markdown: very extended — footnotes, definition lists, citations, raw LaTeX. Popular for academic writing.
Obsidian markdown: GFM + wikilinks ([[like this]]) + embeds. Valid GFM but wikilinks break elsewhere.
Rule: target GFM unless you have a reason not to. It’s the broadest support with the features developers need.
Headings
ATX style (preferred): # H1, ## H2, etc. Clean, linter-friendly.
Setext style: underline with === or --- under the heading text. Only supports H1/H2. Harder to change levels.
One H1 per document. The H1 is usually the title. Start body content at H2.
Don’t skip levels. ## → #### is wrong. Accessibility and TOC generators both break.
Blank lines around headings. Required in strict CommonMark. Avoids accidental continuation into paragraphs.
Lists — the easy-to-mess-up part
Indentation: use 2 or 4 spaces consistently. GFM usually 2; some linters expect 4. Mixing breaks nesting.
Bullet consistency: pick one of -, *, +. Markdownlint enforces one.
Ordered lists: 1. 2. 3. works; so does 1. 1. 1. (renderers renumber). The all-1s style is easier to maintain because you don’t renumber when inserting items.
Blank line before lists. Required in strict CommonMark. Otherwise the list starts mid-paragraph and doesn’t render as a list.
Nested code in lists. Indent the code block to match the list item’s content column. Off-by-one indentation turns the code into regular text.
Code blocks
Fenced (preferred): ```language ... ```. Supports syntax highlighting via the language hint. Always specify the language when it’s code — even ```text for plain text blocks.
Indented: 4 spaces of indent marks a code block. CommonMark spec. Breaks inside lists.
Inline code: single backticks `like this`. If the code contains backticks, use double: ``code with `backticks` inside``.
Language hints: use standard names: js, ts, python, go, rust, bash, shell, sql, json, yaml, html, css, diff. Unknown languages fall through without highlighting.
Links and references
Inline: [text](url). Good for one-off links.
Reference: [text][ref] with [ref]: url at the bottom. Cleaner when the same URL appears multiple times, or URLs are long.
Autolinks (GFM): https://example.com auto-converts. Bare URLs become clickable.
Titles: [text](url "title") — shown on hover. Optional but useful for accessibility.
Image syntax: . Always include alt text for accessibility.
Relative vs absolute: in docs that get rebased (GitHub READMEs served at different paths), relative links break. Use absolute paths in published docs.
Tables (GFM)
Pipe-delimited with a header separator row:
| Column 1 | Column 2 | | -------- | -------- | | Row 1 | Data | | Row 2 | Data |
Alignment: :--- left, :---: center, ---: right in the separator row.
Don’t auto-align cells. Spending time aligning table cells with spaces is wasted — the renderer ignores it, and editing the table later re-breaks alignment.
Long cells: tables with very long cell content render poorly. Consider a definition list or prose if you have more than a short phrase per cell.
Blockquotes and callouts
Standard: > quoted text. Nested quotes: >>.
GFM alerts (new): GitHub added > [!NOTE], > [!WARNING], > [!TIP], > [!IMPORTANT], > [!CAUTION]. Renders as colored callout boxes on GitHub.
Line breaks and paragraphs
Paragraph: blank line between chunks of text.
Hard line break: two spaces at end of line →<br>. Easy to miss in code review (trailing whitespace). GFM also accepts \ at end of line.
Rule: hard breaks are usually wrong. If you want two separate lines, make them separate paragraphs. Hard breaks inside a paragraph are rare in technical writing.
Escaping
Backslash-escape markdown special characters to render them literally: \*not bold\* → *not bold*.
Common escape targets: *, _, #, <, >, [, ], \, \`.
Inside code blocks or inline code, no escaping needed. Everything is literal.
Linting with markdownlint
markdownlint-cli catches: heading spacing, list indentation mismatches, trailing whitespace, inconsistent bullets, bare URLs, duplicate headings, line length (if configured).
Add a .markdownlint.json to project root. Common overrides: disable line-length rule (MD013: false) since prose doesn’t need hard wrapping, and allow multiple H1s if needed (MD025: false).
Prettier also formats markdown. Combine: Prettier for formatting, markdownlint for rules.
Common mistakes
Trailing whitespace for line breaks. Invisible in diffs. Most editors strip it on save. Write as separate paragraphs instead.
Tight lists without blank lines. Breaks renderers. Always put a blank line before the first item.
Forgetting code block language. No highlighting, no copy-button support in many viewers. Always specify.
Deeply nested lists. Past 3 levels, markdown becomes unreadable. Refactor to headings or sub-sections.
Pasting rich text. Copying from Word or Notion often embeds HTML spans and non-breaking spaces. Paste as plain text, then format.
Markdown-in-HTML confusion. Inside a <div>, most renderers don’t parse markdown. Add a blank line inside the div, or use markdown="1" (Kramdown) or equivalent.
Run the numbers
Convert markdown to HTML instantly with the markdown to HTML converter. Pair with the HTML to markdown converter to extract content from web pages, and the HTML formatter to clean up the generated output.
Use these while you read
Tools that pair with this guide
- Markdown to HTML ConverterPaste your Markdown to get clean, structured HTML instantly. Supports tables, code blocks, and task lists — no signup, all conversion happens securely in your browser.Developer Utilities
- HTML to MarkdownPaste HTML and get clean Markdown with preserved headings, lists, links, and code blocks. Ideal for migrating blogs instantly with this free, no-sign-up online tool.File & Format Converters
- HTML FormatterFormat messy HTML into clean, indented code or compress it to a minified version instantly. A free, no-sign-up tool that runs safely in your browser.Developer Utilities
- JSON FormatterPaste JSON to beautify, validate, and minify with clear error messages, all in your browser without sign-up—free instant tool for developers.Developer Utilities
Advertisement
Continue reading
- Developers & TechnicalGitHub Actions Without Being a DevOps ExpertMaster GitHub Actions for the 90% use case with this practical playbook. Build, test, and deploy instantly using free common templates and no-sign-up guides.
- Developers & TechnicalBest Practices for Building Developer ToolsLearn CI/CD, IDE, and documentation standards for paid dev tools instantly. Implement best practices for what companies actually buy online.
- Developers & TechnicalHow to Contribute to Open Source Developer ToolsFind beginner-friendly OSS projects and ship your first pull request with confidence. Free, instant playbook to avoid mistakes and scale contributions.
- Developers & TechnicalHow to Design CLI Tools Developers LoveFree guide to build CLI tools developers actually love: composability, sensible defaults, human errors, trust by default, predictability, fast feedback.
- Developers & TechnicalPassword Security Guide with Real Entropy ExamplesCalculate real password entropy with 2026 attacker speeds. Free guide to diceware passphrases, password managers, and 2FA based on actual attack vectors.
- Developers & TechnicalJSON Format Rules Every Developer Should KnowFree guide to strict JSON spec rules, JSON5 vs JSONC, top 10 parser errors, Schema validation, streaming huge files, and security: prototype pollution.