Skip to content

MD093 - Headings should not contain inline formatting

Aliases: no-formatting-in-headings

This rule is opt-in. Enable it with extend-enable = ["MD093"] in the [global] section of your config.

What this rule does

Reports an inline code span, strong emphasis or emphasis inside a heading, in both ATX and setext form.

Why this matters

A heading is not only text on the page. It is also the source of generated artifacts: a table of contents, an anchor, a sidebar or outline entry, a PDF bookmark. Inline markup reaches those inconsistently — one generator strips the markers, another emits them literally, a third keeps the markup in the page and drops it from the anchor — so the same heading can read Method map() in the body and Method `map()` in the table of contents. Nothing in the source says which will happen, because the answer belongs to the tool that consumes the document rather than to the document.

The other half of the problem is that markup in a heading is often unintentional. A name that contains _ or * becomes emphasis on its own:

## __tests__/gt.test.js

That heading renders as tests/gt.test.js. The underscores are gone from the page, from the anchor and from the table of contents, and the document lints clean, so nothing tells the author that the path they published is not the path they wrote. The same happens to *args, to a heading naming a file such as __init__.py, and to a snake_case_name whose parts are separated by single underscores.

Examples

Incorrect

## Method `map()`

### **Practice**

## The _.env_ file

## __tests__/gt.test.js

Heading with `code`
-------------------

Correct

The identifier spelled without markup:

## Method map()

### Practice

## The .env file

An escape keeps a name that markdown would otherwise read as emphasis:

## \_\_tests\_\_/gt.test.js

Formatting outside headings is untouched, so the identifier can keep its code span in the sentence below the heading:

## Iterating an array

The `map()` method returns a new array.

Configuration

One switch per construct. All three are on once the rule is enabled, because they are three shapes of the same problem, but they have different justifications: a code span in a heading is usually deliberate and only breaks generated artifacts, while emphasis is often an accident of a name. A project that wants only one of them reported turns the other two off.

Option Type Default Description
code boolean true Report inline code spans in headings
strong boolean true Report strong emphasis (**/__) in headings
emphasis boolean true Report emphasis (*/_) in headings
[global]
extend-enable = ["MD093"]

[MD093]
code = false      # keep `identifier` in headings, report only emphasis
strong = true
emphasis = true

What this rule leaves alone

Formatting outside a heading. Only the heading line itself is examined.

Headings inside code blocks. A fenced or indented block is content, and the ## in it is a character rather than a heading.

A link, an image or HTML in a heading. A link in a heading is normal and survives generated artifacts, and inline HTML in a heading is MD033 - No inline HTML's subject. Markup inside a link's text is reported, because the link text is what a table of contents carries: the heading ## [The `map()` docs](url) is a finding on the code span, not on the link.

Automatic fixes

None, by design. Removing the markers changes what renders rather than only how the source reads. Dropping the backticks from a path in a code span can turn the path into bold text, and dropping the emphasis from ## **tests**/sort.test.js yields tests/sort.test.js — a plausible path that was never in the source. Both rewrites are silent corruptions, and which remedy the author meant (rewrite the heading, escape the markers, move the identifier into the text below) is not derivable from the source.