Skip to main content

How to Make a Collapsible Section in Markdown

Markdown has no collapsible syntax, so you borrow HTML's <details> element. This guide covers the syntax, the blank-line rule that decides whether the Markdown inside renders at all, and the native alternatives in Obsidian, Notion, and MkDocs.

Short answer

Wrap the content in <details> and put the clickable label in <summary>. Leave a blank line after the closing </summary> tag — without it the Markdown inside is printed as raw text instead of being formatted. Add open to start it expanded.

<details>
<summary>Click to expand</summary>

Hidden content goes here, and it can use **Markdown**.

</details>

The <details> Syntax

There is no collapsible section in the Markdown spec, and there never has been. What every guide calls “Markdown collapsible sections” is the HTML <details> element, which works because CommonMark deliberately lets raw HTML through. It is three parts:

  • <details> — the container. Everything between the tags is what collapses.
  • <summary> — the always-visible label the reader clicks. Optional, but without one the browser shows a bare “Details” triangle.
  • A blank line after </summary> — the part everyone forgets. See the next section.
The complete pattern

Markdown

<details>
<summary>Click to expand</summary>

Hidden content goes here, and it can use **Markdown**.

</details>

Preview (click it)

Click to expand

Hidden content goes here, and it can use Markdown.

The preview above is the real thing. Because <details> is passed straight through by the renderer, every preview pane on this page is a working disclosure widget — click one and it opens, the same way it will on GitHub.

The Blank-Line Rule

This is the reason collapsible sections go wrong, and almost nothing that ranks for this topic says it out loud. In CommonMark, an HTML block runs until the first blank line. Everything before that blank line is copied to the output untouched — so if your content is pressed up against </summary>, it is still inside the raw HTML, and the parser never sees the asterisks as bold.

No blank line: the whole thing stays raw HTML and **bold** ships as literal asterisks

Markdown

<details>
<summary>Click to expand</summary>
This line uses **Markdown**.
</details>

HTML output

<details>
<summary>Click to expand</summary>
This line uses **Markdown**.
</details>
One blank line: the HTML block ends and the body is parsed as Markdown

Markdown

<details>
<summary>Click to expand</summary>

This line uses **Markdown**.

</details>

HTML output

<details>
<summary>Click to expand</summary>

<p>This line uses <strong>Markdown</strong>.</p>
</details>
Why plain text seems to work anyway. Without formatting, unparsed Markdown and parsed Markdown look identical, so a section that says “Hidden text” renders fine and hides the bug. It surfaces the first time you add a bold word, a list, or a code fence — which is why people report that collapsible sections “stopped working” after they edited them. Put the blank line in from the start.

Starting It Expanded

Add the open attribute to the opening tag. It is a boolean attribute — it takes no value — and the reader can still collapse the section afterwards.

Expanded on load

Markdown

<details open>
<summary>Already expanded</summary>

The reader can still collapse this one.

</details>

Preview (click it)

Already expanded

The reader can still collapse this one.

One place it is quietly dropped. PyPI renders project READMEs through readme_renderer, whose allow-list permits the <details> and <summary> tags but allows only id as a generic attribute. So open is stripped and the section arrives collapsed. Never put the thing a reader must see behind a toggle.

What You Can Put Inside

Once the blank line has ended the HTML block, the content is ordinary Markdown. Headings, lists, tables, images, links, and fenced code blocks with syntax highlighting all behave exactly as they do outside the section.

A heading, a list, and a table

Markdown

<details>
<summary>Everything works in here</summary>

### A heading

A paragraph, a list, and a table:

- one
- two

| Column | Value |
| ------ | ----- |
| rows   | fine  |

</details>

Preview (click it)

Everything works in here

A heading

A paragraph, a list, and a table:

  • one
  • two
Column Value
rows fine
A fenced code block - the most common use in a README

Markdown

<details>
<summary>Show the install command</summary>

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

</details>

Preview (click it)

Show the install command
npm install my-package

Nesting Collapsible Sections

A <details> block can contain another one, as deep as you need. Each level needs its own blank lines around its body — the rule is per-block, not per-document. The usual shape is one outer toggle for a topic and an inner one per platform or per version.

Two levels

Markdown

<details>
<summary>Platform notes</summary>

<details>
<summary>macOS</summary>

Requires Xcode command line tools.

</details>

<details>
<summary>Windows</summary>

Requires the Visual C++ build tools.

</details>

</details>

Preview (click it)

Platform notes
macOS

Requires Xcode command line tools.

Windows

Requires the Visual C++ build tools.

Formatting the Summary Label

The <summary> line sits before the blank line, so it is still raw HTML — which means Markdown written inside it is not parsed. This surprises people who expect **Bold** to work there.

Markdown in the summary is printed as-is

Markdown

<details>
<summary>**Bold summary**</summary>

Body text.

</details>

Preview (click it)

**Bold summary**

Body text.

Inline HTML in the summary works

Markdown

<details>
<summary><b>Bold summary</b> with <code>code</code></summary>

Body text.

</details>

Preview (click it)

Bold summary with code

Body text.

If you want a real Markdown heading as the label, the same blank-line rule that saved the body will save the summary: open the tag, leave a blank line, write the Markdown, leave another blank line, and close the tag. The blank line ends the HTML block long enough for the parser to process what is between.

A Markdown heading inside the label

Markdown

<details>
<summary>

### A heading as the label

</summary>

Body text.

</details>

Preview (click it)

A heading as the label

Body text.

Keep summary labels short and descriptive — Show the full error log rather than Click here. The label is the only thing a reader sees when deciding whether the section is worth opening, and it is what browser find-in-page matches before the section is expanded.

Collapsible Sections Inside a List

Numbered setup instructions with an optional troubleshooting toggle per step is one of the most useful shapes there is, and it is where the indentation trap bites. Indent the block to the list item's content column — two spaces under a bullet, three under a 1. — and never four, which turns the whole thing into a code block.

A toggle attached to step one

Markdown

1. Install the dependencies.

   <details>
   <summary>Troubleshooting</summary>

   Delete `node_modules` and try again.

   </details>

2. Run the build.

Preview (click it)

  1. Install the dependencies.

    Troubleshooting

    Delete node_modules and try again.

  2. Run the build.

Native Alternatives

<details> is the answer on GitHub and GitLab because those are HTML-rendering surfaces. Several other tools grew their own collapsible syntax, and where one exists it is almost always the better choice — it is styled, it nests cleanly, and it does not depend on HTML being allowed.

Obsidian: foldable callouts

Add a - after the callout type to start it collapsed, or a + to start it expanded. Wikilinks, embeds, and nested callouts all work inside.

Obsidian only
> [!note]- Are callouts foldable?
> Yes. A minus sign collapses the callout by default.

> [!tip]+ This one starts open
> A plus sign expands it by default.

Material for MkDocs: ??? admonitions

Start an admonition with ??? instead of !!! and it renders as an expandable block. Requires the pymdownx.details extension in mkdocs.yml.

MkDocs with pymdownx.details
??? note "Collapsed by default"
    Requires the pymdownx.details extension.

???+ note "Expanded by default"
    The plus sign starts it open.

Notion: toggle blocks

Notion drops pasted HTML, so <details> does nothing there. Its equivalent is a toggle block: type > followed by a space, or use /toggle. Note that this is the same keystroke that makes a blockquote in standard Markdown — in Notion a quote is " plus a space.

Chat apps: spoilers, and that is all

Discord, Slack, and Reddit allow no HTML in messages, so there is no collapsible section on any of them. The nearest thing is a spoiler, which hides a run of inline text behind a click but carries no label and no block content. Slack has no equivalent at all.

The closest thing in chat
Discord:  Here is the answer: ||the butler did it||
Reddit:   Here is the answer: >!the butler did it!<

Common Mistakes

Four of these five are about where the blank lines and the indentation go. None of them produces an error message — the section just renders wrong, or swallows your document.

Broken

<details>
<summary>Notes</summary>
This is **bold**.
</details>

Fixed

<details>
<summary>Notes</summary>

This is **bold**.

</details>
The single most common failure. An HTML block runs until the first blank line, so without one the body is still inside the raw HTML and the asterisks are printed literally. A blank line after </summary> ends the HTML block and hands the rest back to the Markdown parser.

Broken

- Step one

    <details>
    <summary>More</summary>

    Hidden text.

    </details>

Fixed

- Step one

  <details>
  <summary>More</summary>

  Hidden text.

  </details>
Four leading spaces make an indented code block, so instead of a disclosure widget you get the raw tags in a monospace box. Inside a list item, indent to the item's content column - two spaces under a bullet, three under a number - not by a fixed four.

Broken

<details>
<summary>**Bold label**</summary>

Body.

</details>

Fixed

<details>
<summary><b>Bold label</b></summary>

Body.

</details>
The summary sits on the second line of the HTML block, before any blank line, so Markdown inside it is never parsed. Use inline HTML instead - <b>, <i>, <code>, <a> - or use the blank-line form shown in the Summary Label section.

Broken

<details>
<summary>Notes</summary>

Hidden text.

## The rest of your document

Fixed

<details>
<summary>Notes</summary>

Hidden text.

</details>

## The rest of your document
A missing </details> does not error - the browser closes it for you at the end of the document, which quietly swallows everything after it into the collapsed section. If a whole page disappears into one toggle, this is why.

Broken

<details open>
<summary>Read me first</summary>

Important setup notes.

</details>

Fixed

<details>
<summary>Read me first</summary>

Important setup notes.

</details>
The open attribute is not universally kept. PyPI's sanitizer allows <details> and <summary> but permits only id as a generic attribute, so open is stripped and the section your users must read ships collapsed. Do not hide anything essential behind a toggle you cannot guarantee is open.

Quick Reference

Collapsible section syntax by tool
SyntaxWhere it appliesStarts
<details>...</details>Anywhere raw HTML is allowed - GitHub, GitLab, MDX, VS CodeClosed
<details open>Same, minus PyPI, which strips the attributeOpen
<summary>Label</summary>The clickable label; inline HTML only, no Markdown
> [!note]- TitleObsidian foldable calloutClosed
> [!note]+ TitleObsidian foldable calloutOpen
??? note "Title"MkDocs with pymdownx.detailsClosed
???+ note "Title"MkDocs with pymdownx.detailsOpen
> + spaceNotion toggle block (not a blockquote)Closed
||spoiler||Discord - inline text only, no labelHidden
>!spoiler!<Reddit - inline text only, no labelHidden

Platform Support

Support tracks one thing: whether the renderer allows raw HTML through. Document and repository platforms do. Chat apps do not, which is why there is no collapsible section in a Discord or Slack message and no workaround that produces one.

Collapsible section support by platform
PlatformSupported?Notes
GitHubYesREADMEs, issues, PRs, discussions, wikis, and gists. <details>, <summary>, and open all survive the sanitizer.
GitLabYesSame syntax, same behavior, in READMEs and issue/MR descriptions.
CommonMark / GFMYesNot a Markdown feature at all - it is raw HTML passthrough, which the spec allows. Any parser with HTML enabled renders it.
MDX (Docusaurus, Next.js, Astro)Yes<details> is valid JSX, so it works as written. Docusaurus styles it to match its admonitions. Blank lines around the body are required here too.
ObsidianPartialHTML <details> renders, but Obsidian's own foldable callouts - > [!note]- - are the better fit and are searchable and linkable.
VS Code previewYesThe built-in preview allows HTML, so the disclosure widget works as it does in a browser.
MkDocs / MaterialPartialRaw HTML works. The idiomatic form is a ??? collapsible admonition, which needs the pymdownx.details extension.
PyPIPartialreadme_renderer allows <details> and <summary>, but its allow-list has no open attribute, so the section always ships collapsed.
Jupyter / R MarkdownYesHTML passes through to the rendered notebook and to knitted HTML output.
NotionPartialPasted HTML is not honored. Notion's native equivalent is a toggle block: type > then a space.
RedditNoHTML is escaped, so the tags appear as literal text. Use a spoiler, >!like this!<, to hide a line.
Discord / SlackNoNo HTML at all. Discord has ||spoilers||; Slack has nothing that hides text.

Frequently Asked Questions

How do you make a collapsible section in Markdown?

Markdown has no collapsible syntax, so you use the HTML <details> element, which every major Markdown renderer passes through. Put a <summary> tag on the line after <details> to hold the clickable label, leave a blank line, write your Markdown, leave another blank line, and close with </details>. The blank lines are what allow the content inside to be parsed as Markdown rather than printed as raw text.

Why is my collapsible section showing **asterisks** instead of bold text?

There is no blank line after the closing </summary> tag. In CommonMark an HTML block continues until the first blank line, so everything pressed up against <summary> is still inside the raw HTML and is emitted verbatim. Adding one empty line after </summary> - and one before </details> - fixes it.

How do I make a collapsible section expanded by default?

Add the open attribute to the opening tag: <details open>. The reader can still collapse it. Note that open is not guaranteed everywhere - PyPI's README sanitizer allows the <details> tag but strips the attribute, so a section you meant to ship expanded arrives collapsed there.

Can I put code blocks, tables, and images inside a collapsible section?

Yes. Once the blank line after </summary> has ended the HTML block, the content inside is ordinary Markdown - fenced code blocks with syntax highlighting, pipe tables, images, lists, headings, and even other collapsible sections all work. This is the usual way to keep a long install log or a screenshot gallery out of the way in a README.

Can you nest collapsible sections in Markdown?

Yes. A <details> block inside another <details> block nests as deeply as you like, and each level needs its own blank lines around its body. Nesting is common for platform-specific instructions: one outer toggle for Installation, and an inner one per operating system.

How do I make the summary label bold or add a heading to it?

Markdown written inside <summary> on the same line is not parsed, because that line is still part of the raw HTML block. Use inline HTML instead - <summary><b>Bold label</b></summary>. If you need a real heading, put a blank line after the opening <summary> tag, write the Markdown, then a blank line before </summary>, which ends the HTML block long enough for the parser to see it.

Does a collapsible section work in a list item?

Yes, as long as you indent it to the list item's content column - two spaces under a bullet, three under a numbered item - and keep the blank lines. Four spaces is the trap: that is an indented code block, so the tags render as literal code instead of a toggle.

Does GitHub support collapsible sections?

Yes, everywhere GitHub renders Markdown: READMEs, issues, pull requests, discussions, wikis, and gists. GitHub's HTML sanitizer allows <details>, <summary>, and the open attribute. This is the single most common place people use them, usually to collapse long logs, changelogs, or optional setup steps.

Do collapsible sections work on Reddit, Discord, or Slack?

No. None of the three allow raw HTML in messages. Reddit escapes the tags so they show up as literal text; Discord and Slack simply have no equivalent element. The closest thing is a spoiler - ||text|| on Discord, >!text!< on Reddit - which hides a run of text behind a click but has no label and no block content.

What is the Obsidian equivalent of a collapsible section?

A foldable callout. Add a minus sign after the callout type to start it collapsed - > [!note]- Title - or a plus sign to start it expanded. Unlike raw HTML, foldable callouts are styled, nestable, and support wikilinks and embeds inside them. HTML <details> also works in Obsidian if the note has to stay portable.

What about Notion, MkDocs, and Docusaurus?

Notion ignores pasted HTML and uses its own toggle block: type > followed by a space. Material for MkDocs has collapsible admonitions - ??? note for collapsed, ???+ note for expanded - which need the pymdownx.details extension enabled. Docusaurus renders plain <details> and styles it automatically, since MDX treats it as a JSX element.

Is content inside a collapsed section still searchable and indexed?

Yes. A collapsed <details> block is present in the HTML, so browser find-in-page, GitHub's code search, and search engine crawlers all see it - it is hidden visually, not removed. Do not use a collapsible section to hide anything sensitive, and do not assume readers will click: if something must be read, leave it in the open.