Give A JSX Data Table Header Cells

ID

javascript.jsx_table_headers

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

accessibility, jsx, reliability

Description

Reports a JSX data table whose rows carry no header cell: there is a <td> in the table and no <th> anywhere in it.

Rationale

A header cell is what makes a table readable without seeing it. A screen reader announces the header along with each cell ("Role: Owner"), so a table of nothing but <td> is read as a flat run of values whose meaning was in the top row the reader cannot see. Bold text in the first row looks like a header and is not one.

A table used purely for layout has no headers by definition. Declaring role="presentation" or role="none" says so and takes the table out of the accessibility tree, which is the correct fix for that case rather than adding headers to it.

Only a table whose data cells are visible here is judged: there has to be a <td> in the subtree before a missing <th> means anything. A table whose rows are built by an expression (<tbody>{rows}</tbody>) shows no cell in this file, and its header row could be in the same expression, so it is left alone rather than guessed at. So is a table containing a component, whose rendered markup is not visible here and is very often the header row itself, and one that spreads an object into its attributes, since role may arrive at runtime.

The searches stop at the first match and cover the whole table subtree, so headers inside a <thead> count wherever they sit.

<table>                                        // FLAW - every cell is a td
  <tr><td>Name</td><td>Role</td></tr>
  <tr><td>Ada</td><td>Owner</td></tr>
</table>

<table>                                        // OK - the first row is headers
  <tr><th>Name</th><th>Role</th></tr>
  <tr><td>Ada</td><td>Owner</td></tr>
</table>

<table role="presentation">...</table>         // OK - declared as layout, not data
<table><TableHead />...</table>                // OK - a component may render the headers

Remediation

Mark the cells that name the columns or the rows as <th> instead of <td>, and put the column headers in a <thead> so the structure is explicit. Add scope="col" or scope="row" to each one so the association is declared rather than inferred. When the table is really a layout grid, either declare role="presentation" on it or replace it with CSS grid or flexbox, which carry no table semantics to correct.

© 2026 Xygeni. All rights reserved