Skip to content

MD022 - Add blank lines around headings

Aliases: blanks-around-headings

What this rule does

Checks that headings have blank lines before and after them to improve readability and document structure.

Why this matters

  • Better readability: Blank lines create visual separation, making content easier to scan
  • Clear structure: Spacing helps readers understand the document hierarchy
  • Consistent rendering: Many Markdown processors require blank lines for proper formatting
  • Professional appearance: Well-spaced documents look more polished and organized

Examples

✅ Correct

Some introductory text here.

# Main Heading

This is the content under the main heading.

## Subheading

More content follows here.

### Another Section

And even more content.

❌ Incorrect

Some introductory text here.
# Main Heading
This is the content under the main heading.
## Subheading
More content follows here.
### Another Section
And even more content.

🔧 Fixed

Some introductory text here.

# Main Heading

This is the content under the main heading.

## Subheading

More content follows here.

### Another Section

And even more content.

Configuration

Basic configuration

[MD022]
lines-above = 1  # Blank lines required above headings (default: 1)
lines-below = 1  # Blank lines required below headings (default: 1)
allowed-at-start = true  # Allow headings at document start without blank line above (default: true)
lines-above-mode = "minimum"  # "minimum" or "exact" (default: "minimum")
# nested-heading-lines-above = 1  # Blank lines before a heading that directly follows its parent (default: unset)

Spacing before a nested heading

Set nested-heading-lines-above to use a different number of blank lines when a deeper heading directly follows another heading without intervening content. This value overrides both lines-above for the child and lines-below for the parent in their shared gap. It accepts a scalar or a six-value array indexed by the child heading's level.

[MD022]
lines-above = 2
lines-below = 1
nested-heading-lines-above = 1

With this configuration, adjacent parent and child headings have one blank line between them. A child heading after content in the parent section still uses the normal lines-above value.

A heading directly follows its parent when it is deeper than the heading before it, sits in the same blockquote or other container, and only these lines come between the two:

  • blank lines
  • HTML and MDX comments
  • block attribute lines, in flavors that recognize them
  • div fences, in the Pandoc flavor

Anything else counts as body, including reference definitions and thematic breaks, so the child gets the normal lines-above value.

In the default lines-above-mode = "minimum", the nested value is a minimum like every other spacing value: more blank lines between a parent and its first child still pass. Set lines-above-mode = "exact" to require exactly that many. MD012 also allows the nested value next to headings, so a nested value larger than lines-above and lines-below raises MD012's limit around every heading.

Exact spacing above headings

By default, lines-above-mode = "minimum" accepts extra blank lines. Use exact mode to also report and remove surplus blank lines immediately above headings:

[MD022]
lines-above = 1
lines-above-mode = "exact"

The mode uses the same per-level lines-above values, document-start exceptions, and inline disables as minimum mode. A value of 0 removes blank lines above that level where safe; a separator before Setext headings or after HTML blocks is retained. -1 leaves spacing unrestricted. Spacing below headings retains its minimum behavior. By default, a gap between adjacent headings satisfies the larger of the child's lines-above and the parent's lines-below values. When nested-heading-lines-above is set, its value replaces both requirements for that gap.

Comment-only lines continue to satisfy the minimum spacing requirement. Exact mode caps the contiguous run of actual blank lines immediately above the heading, or above comments attached directly to it. Earlier gaps between blocks and all comments stay intact. It also preserves blank lines that separate independent blockquotes.

Per-level configuration

You can specify different spacing requirements for each heading level (h1-h6) using arrays:

[MD022]
# Different spacing for each level: [h1, h2, h3, h4, h5, h6]
lines-above = [2, 1, 1, 1, 1, 1]  # h1 needs 2 blank lines above, others need 1
lines-below = [1, 1, 1, 0, 0, 0]  # h4-h6 don't need blank lines below

Use -1 to accept any number of blank lines for a specific level:

[MD022]
lines-above = [-1, 2, 1, 1, 1, 1]  # h1 is unrestricted, h2 needs 2 blank lines
lines-below = [1, -1, -1, -1, -1, -1]  # h1 needs 1, others are unrestricted

This is useful when you want:

  • More space before top-level headings for visual separation
  • Less space around deeply nested headings
  • Different spacing conventions for different document sections

Automatic fixes

This rule automatically adds the required number of blank lines:

  • Before headings (except at the start of a document)
  • After headings (except at the end of a document)

Lists and code fences directly below a heading

lines-below applies before lists, fenced code blocks, and thematic breaks, just as it does before paragraphs. MD022 enforces this requirement even when MD032 or MD031 is disabled. When multiple rules require the same blank line, fixing the document inserts it only once.

HTML comments

A line holding nothing but an HTML comment counts as a blank line, so a comment directly above or below a heading satisfies this rule and rumdl fmt leaves the document alone:

Some text.
<!-- prettier-ignore -->
## Heading

This keeps a directive comment attached to what it applies to: inserting a blank line there would turn the directive off.

The comment has to have the line to itself. Some text. <!-- note --> is a paragraph, so a heading written under it is still missing its blank line. A comment spanning several lines counts at both ends, and the convention holds inside a blockquote. MD031, MD032 and MD058 read blank lines the same way.

Markdown with Gherkin

Under the mdg flavor, MD022 does not require a blank line above a heading that names a Gherkin structure when the line directly above it is a tag line — a line containing a backtick-wrapped tag:

`@billing` `@critical` # checkout tags
## Scenario: Reject an expired card

Recognition follows Cucumber's `(@[^`]+)` scan. A matching tag may occur anywhere on the line, and surrounding text or a trailing comment does not disqualify it.

When the tag line opens the document, Gherkin reads the inserted blank as the Feature line and the Feature collapses into an unnamed node. Lower down the blank leaves the parse intact, and the exemption keeps the tags written against the structure they belong to.

The exemption needs both halves. An ordinary heading such as ## Notes names no structure and keeps the requirement even below a tag line, a line without a matching tag is not a tag line, and the requirement below a heading is never waived.

See Markdown with Gherkin Flavor for the full flavor specification.

Learn more

  • MD001: Use consistent heading levels
  • MD003: Use consistent heading style
  • MD023: Start headings at the beginning of lines
  • MD041: First line should be a top-level heading