HTML Tables
A table displays data in rows and columns, like a spreadsheet. The main tag is table, and inside it you build rows with the tr tag. Each row holds cells written as td, short for table data, and cells that sit in the same position line up into columns automatically.
Header cells use the th tag. Headers describe what each column holds, and the browser renders them bold and centered by default. They matter beyond looks: screen readers use them to announce which column a cell belongs to, which turns a wall of numbers into something blind visitors can follow.
Here is the modern rule: tables are for data, not layout. In the 1990s designers arranged whole pages with invisible tables, and the results were brittle and hard to maintain. CSS handles layout today. Reach for a table when you genuinely have rows of information, like schedules, prices, or scores.
Structure with thead and tbody
Large tables benefit from a little extra structure. The thead tag wraps the header rows, the tbody tag wraps the data rows, and the tfoot tag wraps totals at the bottom. The browser renders the table fine without them, but the markup becomes readable, and browsers can handle long tables more gracefully.
<table>
<thead>
<tr><th>Day</th><th>Hours</th></tr>
</thead>
<tbody>
<tr><td>Monday</td><td>9 to 5</td></tr>
</tbody>
</table>
Borders and spacing
A bare table has no visible lines, just text in a grid. CSS adds the borders that make tables easy to read, and the border-collapse property removes the double lines between adjacent cells. A little padding inside each cell keeps the text from touching the borders.
<table style="border-collapse: collapse;">
<tr>
<td style="border: 1px solid #999; padding: 8px;">Cell</td>
</tr>
</table>
Syntax
<table>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<tr>
<td>Mia</td>
<td>24</td>
</tr>
</table>
Without <caption> + scope="col" Screen Readers Read Tables as Gibberish
Sighted users infer headers visually; screen readers need <caption> for the table's purpose and <th scope="col|row"> to associate cells. Missing them is WCAG 1.3.1 failure flagged by axe. Reddit r/accessibility complaints show NVDA reading 'row 1 column 2 blank' endlessly when scope missing. First row should not be TD faking a header.
<table><caption>Quarterly Sales</caption><tr><th scope="col">Product</th></tr>
Good to know
- table holds the grid, tr makes rows, th and td make cells.
- Wrap header rows in thead and data rows in tbody.
- Use th for headers so screen readers understand the data.
- Style tables with CSS; do not use them for page layout.