<th> Table header cell
Header cell for a column or row, shown bold and centered by default.
<th> marks a header cell. When a screen reader reads a data cell, it announces the headers that belong to it, so the table makes sense without seeing it.
Column headers take scope="col" and row headers take scope="row". With colspan and rowspan a header can stretch across several columns or rows. People often write a bold <td> where a th belongs; that cell is no header.
scope, headers and abbr are documented in the th element in the HTML Living Standard (opens in a new tab). Practise in the Headers and Captions lesson.
Syntax
<th scope="col">Column header</th>When to use it
A cell that labels other cells is a <th>. Column titles in a price list, the names down the left of a comparison chart and the day names in a calendar are all headers. The test is simple enough: if the cell tells you what a column or row is about, it is a header.
Scope follows direction. Use col for a header above its data and row for one beside it. colgroup and rowgroup cover a group set up by colgroup or tbody. Long header text can get a short version in the abbr attribute, which some screen readers repeat in its place: "Avg days" for "Average delivery time in days".
The empty top-left corner of a two-way table labels nothing, so it is not a header. Make it a <td>. Left as an empty th, it can make some screen readers announce a blank header for that whole row or column. You can practise in the Headers and Captions lesson.
Attributes
| Attribute | What it does |
|---|---|
scope | col, row, colgroup or rowgroup: which cells this header labels. |
colspan | Number of columns the cell spans. |
rowspan | Number of rows the cell spans. |
headers | Ids of other header cells that label this cell. |
abbr | Short version of the header text. |
Live example
Edit the code and watch the preview.
<table>
<tr>
<th scope="col">Name</th>
<th scope="col">Score</th>
</tr>
<tr>
<th scope="row">Ana</th>
<td>92</td>
</tr>
</table>More examples
Two-way comparison table
Phones run across the top, features down the side. That gives the table column headers and row headers. The empty corner labels nothing, so it is a plain td.
<table>
<tr>
<td></td>
<th scope="col">Phone A</th>
<th scope="col">Phone B</th>
</tr>
<tr>
<th scope="row">Battery</th>
<td>4,500 mAh</td>
<td>5,000 mAh</td>
</tr>
<tr>
<th scope="row">Weight</th>
<td>180 g</td>
<td>205 g</td>
</tr>
</table>Region headers with rowgroup
Each region gets its own tbody. The region name is a header with scope="rowgroup" that spans the rows in its group. Shop names are row headers, each covering only its own line.
<table>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col">Shop</th>
<th scope="col">Sales</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="rowgroup" rowspan="2">North</th>
<th scope="row">Hilltop</th>
<td>$4,200</td>
</tr>
<tr>
<th scope="row">Riverside</th>
<td>$3,100</td>
</tr>
</tbody>
<tbody>
<tr>
<th scope="rowgroup" rowspan="2">South</th>
<th scope="row">Harbour</th>
<td>$5,600</td>
</tr>
<tr>
<th scope="row">Old Town</th>
<td>$2,900</td>
</tr>
</tbody>
</table>Tip: Mark headers as th with scope, never bold td.
Browser support
th works in all browsers and is defined in the HTML Living Standard. The scope values col and row are well supported by screen readers. Colgroup, rowgroup and the abbr attribute are patchier. Keep complex tables as plain as the data allows, and run them through a screen reader if you get the chance.
Accessibility
Give every header cell a th with scope, and each data cell will be announced with its headers (WCAG 1.3.1). The W3C tutorial on tables with two headers (opens in a new tab) shows scope at work.
Common mistakes
Wrong
<td><b>Price</b></td>Right
<th scope="col">Price</th>Screen readers recognise th as a header. A bold td is only bold text.
Frequently asked questions
What does the scope attribute do on th?
It tells assistive technology which cells a header covers. scope can be col for the cells below, row for the cells to its right, or colgroup and rowgroup for groups. As the user moves through data cells, the screen reader announces the matching header. Browsers can guess for simple tables; an explicit scope is more reliable.
When do I need the headers attribute?
Only in complex tables, where a cell depends on several headers that scope cannot express. Give each <th> an id, then list the relevant ids in the cell's headers attribute, as in <td headers="q1 sales">. If you can, simplify the table or split it into smaller ones. The W3C has a worked example of tables with multi-level headers (opens in a new tab).
Practise it
- Headers and Captions (Module 5: Tables)
- Spanning Cells (Module 5: Tables)
- Project: Class Timetable (Module 5: Tables)
- Full Recipe Page (Module 10: Final Projects)