TS-27: Markdown

Markdown is a lightweight human-editable markup language. It is widely used for simple documentation such as README files, contribution guides, changelogs, pull request descriptions, and code comments, and it is supported by many source code hosting platforms, issue trackers, and chat tools.

For technical documentation that requires tables of contents, cross-references, included files, admonitions, or diagrams, AsciiDoc is RECOMMENDED instead. See TS-28: AsciiDoc.

This technical standard offers tips and best practices for writing Markdown. It recommends GitHub Flavored Markdown (GFM), a strict superset of the CommonMark specification, as the baseline dialect.

For a complete language reference, see the CommonMark Specification and the GitHub Flavored Markdown Specification.

Overview

Markdown is a lightweight markup language with a plain-text formatting syntax. Its design goal is to be readable as-is, without the formatting tags or instructions of a richer markup language, while still allowing the text to be converted to structured formats such as HTML.

Markdown is suited to short, relatively simple documents. For longer or more structured technical documentation, AsciiDoc is RECOMMENDED; see TS-28: AsciiDoc.

When to use Markdown

Markdown is a good choice for:

  • README files and other repository meta-documents.
  • Changelogs, pull request descriptions, and issue comments.
  • Code comments and API documentation that is co-located with source code.
  • Chat and messaging platforms that support Markdown input.

When not to use Markdown

Avoid Markdown when a document needs:

  • A generated table of contents.
  • File includes or modular content.
  • Complex tables, cross-references, or admonitions.
  • Embedded diagrams or conditional rendering.

Prefer AsciiDoc for these cases.

Flavors and specifications

There are many incompatible Markdown dialects. To maximize portability and predictable rendering, documents SHOULD target a single, well-specified dialect.

GitHub Flavored Markdown (GFM) is RECOMMENDED. GFM is a strict superset of the CommonMark specification, adding tables, strikethrough, task lists, and automatic linking of URLs.

Documents SHOULD be valid CommonMark at minimum. Features that are specific to GFM (tables, strikethrough, task lists) MAY be used where the target rendering platform supports GFM (which is not limited to GitHub.com).

Some platforms add their own extensions on top of GFM. GitHub, for example, supports callouts (see Callouts), which are not part of the GFM specification. Such extensions MAY be used where the target platform supports them, but authors SHOULD be aware that they are not portable and will degrade on other renderers.

Avoid dialects that are not backed by a formal specification, such as the original Markdown.pl implementation. Avoid proprietary extensions that are unlikely to be portable across renderers.

File extensions

Markdown files SHOULD use the .md extension.

The extensions .markdown, .mdown, and .mkd MAY be used, but .md is preferred for consistency and broad tool support.

Files that contain only Markdown table data, or that are consumed by a specific tool, MAY use a tool-specific extension.

Headings

Headings SHOULD be written using ATX-style syntax - one to six number signs (#) followed by a single space and the heading text.

# Level 1 heading
## Level 2 heading
### Level 3 heading

Setext-style headings (underlining text with = or -) SHOULD NOT be used. They only support two levels, are harder to edit, and are less widely supported.

A single space MUST be placed between the number signs and the heading text. Some processors do not recognize a heading when the space is omitted.

A blank line SHOULD be placed before and after each heading. Without blank lines, some processors will not parse the heading correctly.

Heading levels MUST increase by single steps. Do not skip from a level 1 heading directly to a level 3 heading.

Single top-level heading

A document SHOULD have a single level 1 () heading, used as the document title. It SHOULD match, or closely reflect, the file name. All subsequent headings SHOULD start at level 2 (#) or deeper.

Many renderers and static site generators treat the first level 1 heading as the document’s title, and a second level 1 heading later in the same document is either ignored or renders as a confusing repeat of the title. A single top-level heading also keeps the document’s own heading hierarchy consistent with the heading levels used for headings.

Unique and descriptive headings

Each heading SHOULD have unique, fully descriptive text, even across different sections of the same document. Do not repeat a heading such as "Overview" or "Configuration" more than once.

Most Markdown renderers generate a link anchor from each heading’s text, by lower-casing it and replacing spaces with hyphens. Duplicate or generic headings produce duplicate or vague anchors, and the renderer typically disambiguates duplicates by appending a numeric suffix (#configuration-1). That suffix is fragile — it depends on document order, so inserting or removing an earlier section shifts every anchor after it, silently breaking existing links.

Paragraphs and line breaks

A paragraph is one or more consecutive lines of text separated from the surrounding content by a blank line.

Paragraphs SHOULD NOT be indented with spaces or tabs. Leading indentation of four or more spaces may be interpreted as a code block.

To insert a hard line break (<br>) within a paragraph, end a line with two or more spaces, or use a backslash (\) at the end of the line. Hard line breaks SHOULD be used sparingly. Prefer separate paragraphs or lists.

Note

Trailing whitespace is invisible in most editors. Configure your editor to highlight trailing whitespace, or use a linter, to avoid accidental hard line breaks.

Emphasis

Use underscores (_) for italics and double asterisks () for bold. Keeping the two markers distinct makes the intent of a run of emphasis obvious in source, where text and text** are easily confused at a glance.

_italic text_
**bold text**
**_bold and italic text_**

Underscores in the middle of a word are not interpreted as emphasis by some processors. Where emphasis must fall inside a word, use a single asterisk (text) instead, which is reliable in all positions.

For GFM, strikethrough is written using two tildes (~~):

~~struck-through text~~

Emphasis SHOULD be applied to words or short phrases, not to entire paragraphs. Do not nest emphasis unnecessarily.

For guidance on using emphasis appropriately in technical writing, see TS-26: Technical writing style guide.

Lists

Unordered lists use a single marker — the hyphen (-) — followed by a single space. Do not mix markers (-, *, +) within the same list.

- First item
- Second item
- Third item

Ordered lists use a number followed by a period and either one or two spaces:

1. First item
2. Second item
3. Third item

Two spaces are RECOMMENDED where list items carry continuation paragraphs or nested blocks. It puts the content at a four-space indent, matching the four-space nesting indent recommended below, so every continuation in the document lines up on the same column:

1.  First item.

    Continuation paragraph, aligned with the item text.

    - A nested item, at the same indent.

Note

CommonMark does not require the numbers in an ordered list to be sequential, but they SHOULD be sequential in source for readability. The rendered output is always sequential.

For a short, stable list, sequential numbering in source (1., 2., 3.) is RECOMMENDED, since it is easy to read directly and rarely needs maintenance. For a long list, or one that is edited frequently, "lazy" numbering — repeating 1. for every item — is RECOMMENDED instead:

1. First item
1. Second item
1. Third item

Lazy numbering avoids renumbering every subsequent item when one is inserted or removed in the middle of the list, which is otherwise a common source of noisy diffs and off-by-one mistakes in source. The rendered output is identical either way, since CommonMark renumbers sequentially regardless of the numbers used in source.

Nested lists are created by indenting child items. Indentation of nested list items MUST be consistent. Four spaces of indentation per nesting level is RECOMMENDED for portability and readability.

To include multiple paragraphs or other block elements in a single list item, indent the continuation lines to align the text with the first line (ie. the indentation size is the number of characters in the list marker plus its trailing space).

A fenced code block inside a list item MUST be indented to that same column, or CommonMark treats it as ending the list item rather than as content within it:

`` 1. First item.

```sh
echo "This code block belongs to the list item."
```
  1. Second item. ``

An indented code block (four-space indentation, without fences) is a valid alternative inside a list item, since its indentation already establishes it as belonging to the item. Fenced code blocks are still RECOMMENDED over indented ones, per Code, provided the fence itself is indented to align with the item’s own content.

Items that wrap onto more than one line SHOULD be separated from each other by a blank line. Once an item is long enough to wrap, it reads as a paragraph, and setting it off with a blank line — the same way paragraphs are set off from each other — materially improves the readability of the plain-text source. Single-line items MAY be kept tight, without blank lines between them.

- This is the first line of the item.
  This is a continuation of the same item.

- This is a separate item.

- This is another item in the same list.

A common pattern is to bold a short lead-in term at the start of each item, followed by a full sentence explaining it. Use the following syntax. The lead-in term is terminated by a full-stop, rather than by a colon or hyphen, for the best accessibility.

* *Performance.* The system responds to user input within 100ms under normal load.

* *Reliability.* The system continues to operate correctly in the presence of hardware or software faults.

* *Scalability.* The system handles growth in data volume, traffic, or complexity without a redesign.

* *Maintainability.* The system is easy for engineers to understand, modify, and extend over time.

Another pattern supported by Markdown is to inject a newline after the lead-in text, and then indent subsequent lines. This has the visual effect of better differentiating the lead-in text, so it is rendered more like a heading.

* *Performance.* +
  The system responds to user input within 100ms under normal load.

* *Reliability.* +
  The system continues to operate correctly in the presence of hardware or software faults.

* *Scalability.* +
  The system handles growth in data volume, traffic, or complexity without a redesign.

* *Maintainability.* +
  The system is easy for engineers to understand, modify, and extend over time.

For lists of key-value pairs, place the colon inside the bold marker.

* *Status:* Active
* *Owner:* Platform team
* *Region:* eu-west-1
* *Version:* 2.4.0

GFM task lists MAY be used where the rendering platform supports them:

- [x] Completed task
- [ ] Outstanding task

Code

Inline code is written with single backticks. Code that itself contains backticks may be enclosed in double backticks, with a space between the backticks and the content for clarity.

Use the `console.log()` function.

Fenced code blocks are written using three backticks on the lines before and after the block. Fenced code blocks are RECOMMENDED over indented code blocks (four-space indentation), as they are easier to read and edit in source.

```json
{
  "name": "example"
}
```

A language identifier SHOULD be specified immediately after the opening fence to enable syntax highlighting where the renderer supports it. Use text or plaintext for examples that do not belong to a specific language. This disables syntax highlighting in renderings.

Escaping newlines in shell snippets

A multi-line shell or command snippet intended to be copy-pasted as a single command SHOULD escape its newlines with a trailing backslash, so that the shell continues the line rather than terminating the command early:

gcloud compute instances create example-instance \
  --zone=us-central1-a \
  --machine-type=e2-medium

Without the trailing backslash, a reader who copies the whole block and pastes it into a terminal gets each line executed as a separate command, which usually fails partway through. This does not apply to output examples or to snippets that are meant to be read rather than executed.

Placeholders

The general convention across these standards is to write placeholder text in angle brackets (<placeholder>), as specified in TS-26: Technical writing style guide. Markdown is a partial exception, where the bracket style depends on whether the placeholder is code-formatted.

Where the placeholder sits inside a code span or a fenced code block, angle brackets SHOULD be used, consistent with the convention across these standards. Nothing inside code formatting is parsed as HTML, so the hazard below does not arise:

git clone git@<hostname>:<group>/<project>.git

Everywhere else — bare prose, headings, table cells — placeholder text MUST be written in square brackets ([placeholder]). Markdown permits raw HTML inline, so outside code formatting a <placeholder> would be interpreted as an unknown HTML element by parsers, and may be stripped or rendered unexpectedly.

YAML front matter MUST follow the same rule as the body: square brackets by default, angle brackets only where the value is code-formatted. The HTML hazard does not arise in front matter, so this is a simplification rather than a safeguard — one rule per document is easier to apply, and easier to check, than one rule for the body and another for its header.

For example:

Run the installer from [project-root], then open [entry-point].

Links are written by enclosing the link text in square brackets, immediately followed by the URL in parentheses.

My favorite search engine is [Duck Duck Go](https://duckduckgo.com).

A link MUST NOT be broken across lines. The whole construct — the bracketed text, the parenthesized URL, and the boundary between them — MUST sit on a single source line, even where that takes the line past the usual line length limit.

A soft wrap between the closing ] and the opening ( breaks the link outright. CommonMark stops treating it as a link and renders the raw syntax as literal text. A wrap inside the bracketed text is less destructive but still puts a line break in the middle of the rendered link text. Where a long URL makes the line uncomfortable, use a reference-style link instead, as described below.

An optional title may be added in double quotes after the URL. The title is displayed as a tooltip in most renderers.

[Duck Duck Go](https://duckduckgo.com "A privacy-focused search engine")

Bare URLs and email addresses SHOULD be enclosed in angle brackets to ensure they are auto-linked:

<https://www.example.com>
<user@example.com>

To prevent a URL from being auto-linked, wrap it in backticks:

`https://www.example.com`

Reference-style links separate the link text from the URL, which can improve readability in documents that contain many long URLs. The link is written as [text][label], and the URL is defined elsewhere as [label]: url "optional title".

See the [CommonMark spec][cm] for details.

[cm]: https://spec.commonmark.org/ "CommonMark Specification"

Reference-style links are most useful where an inline link would disrupt the surrounding prose: a long URL that would push the line well past the usual length, a link that needs to appear inside a table cell (where a long inline URL makes the cell unreadable and can break the table’s column alignment in source), or a destination that is linked to more than once in the same document. For a short link used once, an inline link is simpler and keeps the destination visible at the point of use — reserve reference-style links for the cases above rather than using them by default.

Reference definitions MAY be placed immediately after the paragraph that uses them or at the end of the document. In a longer document, prefer placing each definition just before the next heading that follows its first use — this keeps the definition close to its use without having to scan past that section’s other content, treating each section like a small page. Reserve placing definitions at the end of the document for labels that are reused across multiple sections, where no single section is the natural home for the definition.

Link text SHOULD describe the destination, not the action of clicking it. Write the sentence naturally, then wrap the most descriptive phrase as the link. Avoid generic link text such as "here", "link", or "click here", and avoid using a bare URL as the visible text where a descriptive phrase is available.

✅ See the [CommonMark specification](https://spec.commonmark.org/) for details.
❌ Click [here](https://spec.commonmark.org/) for the spec.

Descriptive link text is more accessible — screen reader users often navigate a page by jumping between links read out of context, where "here" conveys nothing — and it reads correctly even where the underlying URL changes or the document is printed without its hyperlinks.

A link to another file in the same repository SHOULD use an explicit path, including the .md extension, rather than relying on the target platform to infer it:

✅ See the [contribution guide](../docs/CONTRIBUTING.md).
❌ See the [contribution guide](../docs/CONTRIBUTING).

Relative paths that climb outside the current directory (../) SHOULD be avoided except where the target is genuinely close by. Prefer an explicit path rooted at a stable location — such as the repository root, where the hosting platform supports it — over a chain of ../../../ segments, which is fragile if either file is later moved and hard for a reader to verify by eye.

URL encoding

Spaces and parentheses in URLs can cause parsing problems. Spaces in URLs SHOULD be URL-encoded as %20. Where a URL contains parentheses, consider using reference-style links or HTML <a> tags.

As with AsciiDoc (see TS-28: AsciiDoc), a link is either internal or external. An internal link is one to another document within the same repository, eg. another file in a docs/ directory or a sibling README; an external link is everything else, including a link to a different repository.

Internal links MUST be bold; external links MUST NOT be. The bold markup MUST sit tight around the link text, inside the square brackets, not wrapped around the whole link construct:

✅ [**Style guide**](./docs/style-guide.md)
❌ **[Style guide](./docs/style-guide.md)**

A reader can then tell from the styling alone whether a link keeps them inside the current documentation set or takes them elsewhere.

Images

Images are written with an exclamation mark, followed by alternative text in square brackets, and the image path or URL in parentheses. An optional title may follow in double quotes.

![Alt text](/assets/images/diagram.png "System diagram")

All images MUST have alternative text. The alternative text SHOULD concisely describe the content of the image; see TS-26: Technical writing style guide for guidance.

To link an image, wrap the image syntax in a link:

[![Alt text](/assets/images/logo.png)](https://www.example.com)

Markdown does not provide a standard way to set image dimensions. Where sizing or alignment is required, use an HTML <img> tag with explicit width and height attributes.

Reference-style images

Images MAY use the same reference-style form as reference-style links (see Reference-style links) — the image syntax with a label in place of the inline URL, and the URL defined elsewhere in the document:

![Alt text][logo]

[logo]: /assets/images/logo.png "Company logo"

This is most useful where the same image is referenced from multiple places in a document, or where an inline URL would disrupt the surrounding prose.

When to use images

Use images sparingly, and prefer simple screenshots over elaborate diagrams or composited graphics. Reach for an image where it is genuinely easier to show a reader something than to describe it in text — a UI navigation path, a layout, or a visual state — rather than as decoration.

An image cannot be searched, diffed meaningfully in version control, or read by a screen reader beyond its alternative text. Where a diagram is a better fit than a screenshot, and the document’s rendering target supports it, prefer AsciiDoc; see TS-28: AsciiDoc.

Blockquotes

A blockquote is written by prefixing a line with a right angle bracket (>) and a single space.

> This is a quoted paragraph.

Blockquotes with multiple paragraphs require a > on each blank line between paragraphs:

> First paragraph.
>
> Second paragraph.

Blockquotes MAY be nested by adding additional > characters. Other block elements, such as lists and code blocks, MAY be included inside a blockquote by prefixing their lines with >.

Blockquotes SHOULD be surrounded by blank lines.

Callouts

Callouts, also known as alerts or admonitions, are a GitHub extension that renders a blockquote as a highlighted, labeled panel. A callout is written as a blockquote whose first line is a bracketed, exclamation-prefixed keyword.

> [!NOTE]
> Useful information that readers should know.

Five keywords are supported: [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], and [!CAUTION]. The keyword MUST be uppercase and on its own line, with the callout content following on subsequent blockquote lines.

Callouts MAY be used in documents targeting GitHub. Because they are a GitHub extension rather than part of the GFM specification, they are not portable: on renderers that do not support them, they degrade to an ordinary blockquote containing the literal keyword text. For admonition-heavy technical documentation that must render across platforms, AsciiDoc is RECOMMENDED instead. See TS-28: AsciiDoc.

Tables

Tables are a GFM extension. They are written with a header row separated by hyphens, and cells separated by pipes.

| Syntax      | Description |
| ----------- | ----------- |
| Header      | Title       |
| Paragraph   | Text        |

A pipe SHOULD be placed at the start and end of each row for portability. The hyphens in the separator row need not be aligned with the column widths; the rendered output is unaffected.

Column alignment is controlled by colons in the separator row:

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

Tables SHOULD be used sparingly in Markdown. Markdown tables do not support cell spanning, and block-level elements such as lists, headings, or nested tables cannot be placed inside cells. For complex tables, use AsciiDoc; see TS-28: AsciiDoc.

Inline Markdown formatting — emphasis, bold, inline code, and links — MAY be used within a table cell, and renders as expected:

| Option     | Description                     |
| ---------- | -------------------------------- |
| `--force`  | Skip the **confirmation** prompt. |

This is distinct from block-level elements, which cannot be placed inside cells at all, per the constraint above.

To display a literal pipe character inside a cell, use its HTML character reference (|).

Tables versus lists

Before writing a table, consider whether a list would present the same information more clearly. A table is a poor fit where a column carries only one or two distinct values across all rows, where the row-to-column ratio is heavily skewed (many rows against few columns, or vice versa), or where a cell’s content is a full sentence rather than a short value — rambling prose in a cell is harder to scan than the same content in a bulleted list.

❌ | Feature       | Supported |
   | ------------- | --------- |
   | Dark mode     | Yes       |
   | Offline mode  | No        |
   | Notifications | Yes       |

✅ Supported features: dark mode, notifications.
   Not supported: offline mode.

A table earns its place where the reader genuinely needs to compare values across two or more dimensions at a glance. Where the content is closer to a flat enumeration, prefer a list.

Horizontal rules

A horizontal rule is created with three or more asterisks (*), hyphens (---), or underscores (_) on a line by themselves.

Hyphens (---) are RECOMMENDED for consistency. A blank line MUST precede a horizontal rule; without one, a line of --- immediately after text may be interpreted as a setext heading.

Horizontal rules SHOULD be used sparingly to separate sections. Prefer headings for structuring content.

Escaping characters

A literal character that would otherwise be interpreted as Markdown formatting may be escaped by prefixing it with a backslash (\).

\* This is not a list item.

The following characters MAY be escaped:

\  `  *  _  { }  [ ]  < >  ( )  #  +  -  .  !  |

Escaping SHOULD be used only where necessary. Prefer to restructure content to avoid ambiguity — for example, by adding blank lines around elements — rather than escaping many characters.

HTML

Most Markdown processors allow inline HTML. HTML is useful where Markdown lacks an equivalent, such as for setting image dimensions, controlling text color, or using <details> disclosure blocks.

This <em>word</em> is italic.

Block-level HTML elements such as <div>, <table>, and <pre> MUST be separated from surrounding Markdown by blank lines, and SHOULD NOT be indented.

Important

Markdown syntax is not processed inside block-level HTML tags. bold inside <div>bold</div> will be rendered as literal asterisks, not as bold text.

HTML SHOULD be used sparingly. Documents that rely heavily on HTML lose the portability and readability benefits of Markdown. For content that requires rich HTML, consider whether a more capable format such as AsciiDoc or direct HTML is more appropriate.

Embedding video

Markdown has no native syntax for embedding video, and most renderers strip <video> and <iframe> embeds for security reasons. The common workaround is a linked thumbnail image — an <a> tag wrapping an <img> of the video’s poster frame, pointing at the video’s hosted page:

<a href="https://www.youtube.com/watch?v=<video-id>">
  <img src="https://img.youtube.com/vi/<video-id>/0.jpg" alt="Video title" width="480" height="360">
</a>

Clicking the thumbnail takes the reader to the video on its hosting platform rather than playing it inline. This keeps the document portable and avoids the security and privacy concerns of embedding third-party iframes directly.

Footnotes

Footnotes are an extension supported by GFM and several other processors, but not by pure CommonMark. A footnote reference is written as [^label], and the footnote content is defined as [^label]: text.

Here is a statement.[^1]

[^1]: The supporting reference.

Footnote labels only correlate the reference with the definition; the rendered output is numbered sequentially regardless of the label.

Because footnote support is not universal, footnotes SHOULD be avoided in documents that must render across a wide range of Markdown processors. Prefer inline links for citations in portable documents.

Line length and wrapping

Lines SHOULD NOT exceed 80 characters. Content longer than this SHOULD be "soft wrapped" — that is, continued on the next line without a trailing backslash or trailing spaces. CommonMark joins consecutive lines within a paragraph into a single line in the rendered output, so soft wrapping does not affect how the document renders.

The purpose of this constraint is to ensure that files render identically in every editor, regardless of the editor’s own line-wrapping width or settings.

Lines MUST NOT exceed 160 characters, except in very special circumstances where this cannot be achieved — for example, in tables or long URLs that cannot themselves be broken across lines.

Headings and fenced code blocks are further exceptions. A heading MUST NOT be wrapped across multiple lines — Markdown has no continuation syntax for a heading, so a soft wrap would either break it or become part of the heading text. The content of a fenced code block MUST be reproduced verbatim, including any line that is long in the source being shown — reformatting code to fit the 80-character guideline would misrepresent it.

A soft wrap MUST NOT be inserted within an inline formatting unit, such as bold or italic text, so as to preserve the formatting markers (eg. ** or _) alongside the text they format.

The same applies to links, which MUST NOT be broken across lines. Where keeping a link intact takes a line past 80 characters, the link wins.

Hard line breaks (two trailing spaces or a trailing backslash) SHOULD be used only where a break is semantically required, such as in postal addresses or song lyrics. Prefer the trailing backslash format, over two trailing spacings, for clarity and to avoid auto-formatters breaking the formatting by removing end-of-line trailing whitespace.

Note

Configure your editor to strip trailing whitespace on save, and to highlight any trailing whitespace that introduces a hard line break. This prevents accidental, invisible line breaks.

Tooling

A linter SHOULD be used to enforce consistent Markdown style across a project. markdownlint is RECOMMENDED. It is configurable and widely supported.

A code formatter such as Prettier MAY be used to automate wrapping, list indentation, and code-block formatting. Formatting and linting SHOULD be reconciled so they do not produce conflicting results.

A link checker SHOULD be run as part of continuous integration to detect broken internal and external links.

Editor configuration SHOULD ensure:

  • Trailing whitespace is highlighted.
  • Files end with a single newline character.
  • Indentation uses spaces, not tabs.

References