Tables (<table>)
The <table> element represents tabular data — information naturally organized into rows
and columns, where each cell relates to a row heading and a column heading. Think spreadsheets,
price comparisons, schedules, and statistical data.
Tables are for data, full stop. Using a table purely to arrange page layout (a technique common in the 1990s and early 2000s) is considered a serious anti-pattern today.
The Golden Rule: Data, Never Layout
<table> to lay out a page (navigation bars, multi-column content grids, form alignment). CSS Grid and Flexbox exist precisely so layout no longer needs tables. Screen readers announce table semantics ("row 2 of 5, column 3 of 4") — forcing that onto layout markup is confusing and actively harmful for accessibility.A Basic Table
<table>
<tr>
<th>Plan</th>
<th>Price</th>
<th>Storage</th>
</tr>
<tr>
<td>Free</td>
<td>$0</td>
<td>5 GB</td>
</tr>
<tr>
<td>Pro</td>
<td>$12/mo</td>
<td>500 GB</td>
</tr>
</table><tr> (table row). Each cell inside is either <th> (table header — bold, centered by default) or <td> (table data — a plain cell). We'll cover both in depth on the next page.Why Semantics Matter
A properly-marked-up table gives assistive technology (and search engines) a data model to
navigate, not just a visual grid. A screen reader user can jump directly to a column header,
ask "what row am I in", or have a cell's header read aloud automatically — none of which is
possible with a table faked out of <div>s.
Use case | Right tool |
|---|---|
Comparing prices across plans |
|
A page-wide grid of navigation links | CSS Grid / Flexbox |
Financial or scientific datasets |
|
A photo gallery layout | CSS Grid |
A schedule or timetable |
|
The Building Blocks Preview
A fully-structured table uses more than just <tr>/<td>/<th>. The following elements —
each covered in their own page — combine to describe a complete, accessible table:
<caption>— a title/description for the whole table (accessible name).<thead>,<tbody>,<tfoot>— group rows into header, body, and footer sections.<colgroup>/<col>— apply styling to entire columns without repeating it per cell.<tr>— a table row.<th>/<td>— header and data cells;scopeassociates a<th>with its row or column.colspan/rowspan— merge cells across multiple columns or rows.
A Slightly Larger Example
<table>
<caption>Monthly Active Users by Region (in thousands)</caption>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col">Jan</th>
<th scope="col">Feb</th>
<th scope="col">Mar</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">North America</th>
<td>842</td>
<td>861</td>
<td>905</td>
</tr>
<tr>
<th scope="row">Europe</th>
<td>610</td>
<td>598</td>
<td>640</td>
</tr>
</tbody>
</table><th scope="row"> used for the region names in the body — a row can have its own header too, just like a column does. We'll dig into scope properly on the Rows & Cells page.Styling Tables With CSS
HTML tables come with minimal default styling (borders around cells only in some browsers), so CSS handles the visual presentation entirely — borders, zebra striping, spacing.
table {
border-collapse: collapse; /* merge adjacent borders into single lines */
width: 100%;
}
th, td {
border: 1px solid #ddd;
padding: 8px 12px;
text-align: left;
}
tbody tr:nth-child(even) {
background-color: #f9f9f9;
}Plan | Price | Storage ------|---------|-------- Free | $0 | 5 GB Pro | $12/mo | 500 GB
Key Takeaways
<table> is for tabular data — rows and columns of related information — never for page layout.
A minimal table needs only <tr> rows containing <th>/<td> cells.
Proper table semantics (caption, scope, thead/tbody) make data genuinely usable by screen readers, not just sighted users.
CSS (border-collapse, padding, nth-child striping) handles all visual presentation — HTML handles structure only.
The next several pages build out each structural piece: rows/cells, sections, captions, spanning, and accessibility.