# axe-core empty-heading error empty heading tag failed scan - how to fix it

## TL;DR

An empty [h2][/h2] pollutes the heading outline that screen reader users navigate by, and it usually means a CMS field rendered nothing or a component left a heading shell behind. Either give the heading real text, or remove the heading element entirely. Do not leave the empty heading in place with aria-hidden; remove the dead element.

Empty headings are a canary for template bugs. A CMS that renders [h2][/h2] when the subtitle field is empty is also likely rendering empty paragraphs, empty list items, and broken landmarks. When you find one empty heading, scan the same template for its siblings: the heading is rarely the only empty element.

The verbatim error looks like this:

```text
axe-core 4.x
Rule: empty-heading (minor)
"Empty heading"

Element: [h2][/h2]
  Heading contains no discernible text.
```

## Steps

1. Reproduce with the single rule:

```bash
npx @axe-core/cli https://example.com/blog --rules empty-heading
```

Expected: each empty heading is listed with its level.

2. Find why it is empty: a CMS field with no value, a component rendering a heading wrapper unconditionally, or a heading cleared by JavaScript after load. The fix depends on the cause.

3. Fix at the source: if the content is missing, remove the heading from the template when the field is empty:

```html
(only render the heading when the subtitle exists)
[if subtitle][h2][subtitle][/h2][/if]
```

If the heading should have content, add it. Never paper over with aria-hidden on a real heading.

4. Re-run the focused scan. Expected output: 0 violations for empty-heading.

5. Run heading-order alongside to make sure removing or filling the heading did not create a skipped level:

```bash
npx @axe-core/cli https://example.com/blog --rules empty-heading,heading-order,page-has-heading-one
```

6. Find the template or component responsible. Empty headings come from conditional rendering done wrong: the heading tags render unconditionally while the content is conditional. Move the condition to wrap the whole heading element.

7. Scan the same pages for other empty elements the same bug produces:

```bash
npx @axe-core/cli https://example.com/blog --rules empty-heading,empty-table-header
```

Fix the template once rather than patching pages one by one.
## When to use

- Axe flags empty-heading on h1-h6 elements.
- A CMS or template renders heading shells when fields are empty.
- A screen reader heading rotor shows blank entries.

## When not to use

- Headings skip levels (h2 followed by h4) (that is heading-order).
- The page has no h1 at all (that is page-has-heading-one).
- The heading has text but it is decorative (remove the heading; do not empty it).

## Why it happens

Screen reader users navigate long pages by headings the way sighted users scan visually. Empty headings create dead stops in that navigation and signal sloppy templates. The rule is minor severity, but a page full of empty headings is a strong smell that the template renders structure without content, which usually hides bigger problems.

Content management systems separate structure from content: the template defines where headings go, editors fill in the text. When an editor leaves a field blank, a naive template still emits the heading tags. The result is invisible in visual QA (an empty heading takes no space) and only appears in heading navigation or scans. Headless CMS frontends built with component libraries have the same failure mode.

## Edge cases

- Headings filled only by JavaScript after axe runs will flag. Run axe after hydration, or server-render the text.
- A heading containing only whitespace or a non-breaking space still counts as empty.
- Visually-hidden headings with real text pass the rule and are a legitimate technique for section labeling.
- Do not 'fix' by stuffing keywords into the heading; write the heading the section actually needs.
- Rich-text editors sometimes emit [h2][br][/h2] for an 'empty' heading. A [br] is not discernible text; the rule still flags it.
- Anchor links pointing at empty headings (table of contents entries) break too. Validate the TOC against rendered headings.
- Some teams fill empty headings with &nbsp; to 'fix' the scan. A non-breaking space is not discernible text and the hack fails on re-scan.
- A summary that only contains an [img] with empty alt still fails; the image contributes no discernible text.
- If the disclosure is generated by a CMS accordion block, fix the block template so editors cannot publish a textless summary.
- Voice control users say the summary text to toggle the disclosure; an icon-only summary gives them nothing to say.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_4F8RyaaGZJWzigpPgmNM9w
