Provide Text For A JSX Heading
ID |
javascript.jsx_empty_heading |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
JavaScript |
Tags |
accessibility, jsx, reliability |
Description
Reports a JSX heading (<h1>..<h6>) that renders nothing and carries no accessible name of its
own.
Rationale
Screen-reader users navigate a page by its headings, so the heading list is the page outline. An empty heading puts an unlabelled entry in that list: it breaks the outline, announces nothing, and gives no clue what the section below it is.
In a component tree it is easy to produce by accident — a heading rendered before its data arrives, or a wrapper kept "for the styling".
What counts as content is what React would put in the DOM: text with something other than
whitespace in it, a nested element or fragment, or an expression in braces. A brace pair holding
only a comment renders nothing and is treated as empty. An aria-label or aria-labelledby names
the heading without visible text and satisfies the rule; an element that spreads an object into its
attributes is left alone, since either may arrive at runtime.
<h1></h1> // FLAW - nothing to announce
<h2 className="title" /> // FLAW - self-closing, so no content at all
<h3> </h3> // FLAW - whitespace is not content
<h1>Dashboard</h1> // OK
<h2>{title}</h2> // OK - computed text
<h3><Icon /> Settings</h3> // OK - a nested element can name it
<h4 aria-label="Filters" /> // OK - named by the attribute
Remediation
Give the heading its text. When the text arrives with the data, render the heading only once it is
there rather than rendering it empty. When the heading is deliberately invisible, name it with
aria-label, or point aria-labelledby at the element that already carries the text. When the
element was only ever a styling hook, use a <div> — it carries no heading role to leave
unlabelled.