The <tr>, <td> & <th> elements
Tables are built from rows and cells. <tr> creates a table row, <td> creates a data cell inside it, and <th> creates a header cell that labels a row or column, shown bold and centered by default. Cells can stretch across several columns or rows with the colspan and rowspan attributes.
Last updated: October 3, 2026
Quick facts
| Category | None of their own — they are only allowed inside tables |
| Content model | <tr>: zero or more <td>/<th>. <td>: flow content. <th>: flow content, but no headings, sectioning, <header> or <footer> |
| Tag omission | End tags may be omitted when another row or cell follows, but writing them is best practice |
| Permitted parents | <tr>: <table>, <thead>, <tbody> or <tfoot>. <td>/<th>: a <tr> |
| DOM interfaces | HTMLTableRowElement, HTMLTableCellElement (<td> and <th>) |
Syntax
Live example
Edit the code below and watch the preview update instantly.
Attributes
<td> and <th>
| Attribute | Description |
|---|---|
| colspan | How many columns the cell spans (default 1) |
| rowspan | How many rows the cell spans (default 1) |
| headers | Space-separated ids of the <th> cells that label this cell, for complex tables |
<th> only
| Attribute | Description |
|---|---|
| scope | Which cells this header labels: col, row, colgroup or rowgroup |
| abbr | A short version of the header text for screen readers to repeat |
<tr>
| Attribute | Description |
|---|---|
| — | Only the global attributes apply. |
Common examples
Row and column headers
scope says whether a th is a header for its column or its row.
Merging cells
colspan spans columns and rowspan spans rows.
Adding rows with JavaScript
insertRow and insertCell create rows without writing HTML strings.
Cells that stack on phones
On small screens, show each row as a card with the column name before each value.
scope vs the headers attribute
Screen readers need to know which header belongs to each data cell. For normal tables with one header row and/or one header column, scope="col" and scope="row" on your <th> cells are all you need.
For complex tables — several header levels or merged header cells — give every th an id and list the matching ids in each td’s headers attribute. It’s precise but laborious, so it’s often better to split a complex table into simpler ones.
| scope | headers | |
|---|---|---|
| Goes on | <th> | <td> (lists th ids) |
| Effort | Low | High |
| Best for | Simple tables | Complex, multi-level tables |
Common mistakes to avoid
- Bold td cells instead of th. Bold styling doesn’t make a header; screen readers won’t know.
- Rows with different cell counts. Without matching colspan, columns shift and the table breaks.
- Empty cells for spacing. Use CSS padding, not extra cells.
- Tables for layout. Arrange page sections with CSS grid or flexbox.
Accessibility & SEO tips
- Keep tables simple — one header row and maybe one header column are easiest for everyone to read.
- Abbreviated headers can carry the full name in an
abbrattribute, for example<th abbr="Quantity">Qty</th>. - Real HTML tables (not images of tables) can be indexed by search engines and shown as table snippets in results.
Browser support
Supported in every browser, including Chrome, Edge, Firefox, Safari and their mobile versions — it has been part of HTML since the very first versions.
Tutorial: How to make a table