About This HTML Issue
A th element can't contain a heading, from h1 to h6, at any depth. Put the text straight in the th, style it with CSS, or title the table with a caption or a heading before it.
The HTML standard defines the content of a th as flow content with no header, footer, sectioning content or heading content (h1 to h6 and hgroup) among its descendants. The rule applies at any depth, so a heading wrapped in a div inside the th still triggers this error. The message names the heading level the validator found, and the fix is the same for every level. A td doesn't have this restriction: headings are allowed in data cells.
A th is already a header. Screen readers announce it when they read the cells of its row or column, so a heading inside it adds nothing to the table. It does add an entry to the page's heading outline, and users who move through the page by headings land inside the table as if a new section started there.
Headings usually end up in header cells to make the text bigger or bolder. Browsers already show th text in bold, and CSS can change the size, weight or letter case.
How to fix it
- Remove the heading element and keep its text directly inside the
th. - Style the
thwith CSS if the header cells need a particular look. - Use a
captionif the heading was meant as a title for the whole table. Acaptionis tied to the table, and screen readers announce it with it. - Put a heading before the
tableif the table needs an entry in the page's outline, at a level that fits the surrounding headings.
Examples
❌ Invalid: heading inside a th
<table>
<tr>
<th><h2>Product</h2></th>
<th><h2>Price</h2></th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
Each h2 is a descendant of a th, which the th content model doesn't allow.
✅ Valid: plain text in the th, styled with CSS
<table class="price-table">
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
.price-table th {
font-size: 1.2em;
text-transform: uppercase;
}
✅ Valid: a caption as the table title
<table>
<caption>Product pricing</caption>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
✅ Valid: a heading before the table
<h2>Product pricing</h2>
<table>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
Find issues like this automatically
Rocket Validator scans thousands of pages in seconds, detecting HTML issues across your entire site.