Tables are for displaying data in rows and columns — think spreadsheets, schedules, and comparison charts. They’re often misused for page layout, but that role belongs to CSS. Use tables only for tabular data.
A table is built from three nested tags: <table> wraps everything, <tr> defines each row, and <td> defines each data cell within a row.
<table>
<tr>
<td>HTML</td>
<td>Structure</td>
<td>1991</td>
</tr>
<tr>
<td>CSS</td>
<td>Style</td>
<td>1996</td>
</tr>
</table>
Every row must have the same number of cells, or the table will render unevenly.
Use <th> instead of <td> for column or row headers. The browser renders them bold and centred by default, and screen readers announce them as headers — making the table far easier to navigate.
<table>
<tr>
<th>Language</th>
<th>Purpose</th>
<th>Year</th>
</tr>
<tr>
<td>HTML</td>
<td>Structure</td>
<td>1991</td>
</tr>
<tr>
<td>CSS</td>
<td>Style</td>
<td>1996</td>
</tr>
</table>
For anything beyond a tiny table, split the rows into semantic sections using <thead>, <tbody>, and <tfoot>. This helps browsers, screen readers, and CSS target each section independently.
<table>
<thead>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Notebook</td>
<td>2</td>
<td>$4.00</td>
</tr>
<tr>
<td>Pen</td>
<td>5</td>
<td>$2.50</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Total</td>
<td>7</td>
<td>$6.50</td>
</tr>
</tfoot>
</table>
Tip:
<tfoot>always goes in the HTML before<tbody>, even though it renders at the bottom. This lets the browser display the footer before all the body rows have loaded on long tables.
A cell can stretch across multiple columns with colspan, or multiple rows with rowspan. The value is the number of cells to span.
<table>
<tr>
<th colspan="2">Name</th>
<th>Score</th>
</tr>
<tr>
<td>First</td>
<td>Last</td>
<td>98</td>
</tr>
</table>
<table>
<tr>
<td rowspan="2">Monday</td>
<td>9am — Maths</td>
</tr>
<tr>
<td>10am — English</td>
</tr>
</table>
Rule: when using
colspanorrowspan, reduce the number of<td>tags in the affected rows accordingly, or you’ll end up with extra unwanted columns.
For complex tables, add a <caption> as the first child of <table>. It acts as a title for the table and is announced by screen readers before the data.
<table>
<caption>Web language comparison</caption>
<thead>
<tr>
<th>Language</th>
<th>Purpose</th>
<th>Year</th>
</tr>
</thead>
<tbody>
<tr>
<td>HTML</td>
<td>Structure</td>
<td>1991</td>
</tr>
</tbody>
</table>
<body>
<h1>My first table</h1>
<table>
<caption>Top 3 programming languages</caption>
<thead>
<tr>
<th>Language</th>
<th>Primary use</th>
<th>Difficulty</th>
</tr>
</thead>
<tbody>
<tr>
<td>HTML</td>
<td>Web structure</td>
<td>Beginner</td>
</tr>
<tr>
<td>Python</td>
<td>Data & scripting</td>
<td>Beginner</td>
</tr>
<tr>
<td>Rust</td>
<td>Systems programming</td>
<td>Advanced</td>
</tr>
</tbody>
</table>
</body>
Note the & in the second row — this is an HTML entity for the & character. Special characters like <, >, and & need to be escaped in HTML content.
| Tag | Purpose |
|---|---|
<table> | Table container |
<tr> | Table row |
<td> | Data cell |
<th> | Header cell (bold, centred) |
<thead> | Header section |
<tbody> | Body section |
<tfoot> | Footer section |
<caption> | Table title (accessibility) |
colspan="N" | Span N columns |
rowspan="N" | Span N rows |