Tables: Data Representation
Building structured data grids with table, tr, th, td, and semantic sections
Tables are for tabular data - pricing grids, schedules, sports scores, comparison charts. They are not for page layout. Understanding both what tables are for and what they are not for is as important as knowing the syntax.
The Table Container (<table>)
The <table> tag is the root container. By itself it is invisible - just an empty grid environment. Think of opening a blank Excel spreadsheet. You see the grid, but there is no content yet.
<table>
<!-- rows and cells go here -->
</table>
History note: In the late 1990s, developers used
<table>to build entire website layouts - logo in cell A1, navigation in cell B1. This was terrible for mobile phones and screen readers. Modern rule: tables are only for tabular data.
Table Rows (<tr>)
Tables are built row by row. The <tr> tag defines one horizontal row of cells. Think of writing in a lined notebook - you fill in one line before moving to the next.
<table>
<tr>
<!-- Row 1 -->
</tr>
<tr>
<!-- Row 2 -->
</tr>
<tr>
<!-- Row 3 -->
</tr>
</table>
A <tr> can only contain <th> or <td> elements - never plain text directly.
Table Headers (<th>)
The <th> tag defines a header cell. Its content is bold and centered by default. Think of the top row of a scoreboard - "HOME" and "AWAY" are not scores, they are labels that explain what the numbers below mean.
The scope Attribute
This tells screen readers whether the header applies to the column below it or the row next to it:
<tr>
<th scope="col">Product</th>
<th scope="col">Price</th>
<th scope="col">Stock</th>
</tr>
| Value | Meaning |
|---|---|
scope="col" |
This header labels the entire column below |
scope="row" |
This header labels the row it sits in |
Table Data (<td>)
The <td> tag defines a standard data cell - where your actual content lives. Think of a P.O. Box: the table is the wall of boxes, the row is a horizontal bank of boxes, and the <td> is the single compartment where the letter is stored.
<tr>
<td>Apple</td>
<td>$1.00</td>
<td>In stock</td>
</tr>
Merging Cells
colspan - stretches a cell horizontally across multiple columns (like knocking down the wall between two hotel rooms):
<td colspan="2">Subtotal</td>
rowspan - stretches a cell vertically down multiple rows:
<td rowspan="3">Photo</td>
Semantic Table Sections (<thead>, <tbody>, <tfoot>)
Professional tables are divided into three semantic sections. This helps browsers and screen readers handle large datasets efficiently.
| Tag | Contains | Benefit |
|---|---|---|
<thead> |
Header rows (<th>) |
Repeats on every printed page; stays fixed while body scrolls |
<tbody> |
Data rows (<td>) |
Can scroll independently; handles thousands of rows |
<tfoot> |
Summary/total rows | Always stays at the bottom |
Complete Professional Example
<table>
<thead>
<tr>
<th scope="col">Item</th>
<th scope="col">Quantity</th>
<th scope="col">Cost</th>
</tr>
</thead>
<tbody>
<tr>
<td>Hammer</td>
<td>2</td>
<td>$10.00</td>
</tr>
<tr>
<td>Nails</td>
<td>1 box</td>
<td>$5.00</td>
</tr>
<tr>
<td>Sandpaper</td>
<td>3 sheets</td>
<td>$3.00</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">Total</td>
<td>$18.00</td>
</tr>
</tfoot>
</table>
Pro tip: Always define
<thead>first in your code, even though<tfoot>renders at the bottom. The browser prefers to know the head and foot first so it can calculate the layout before loading all the heavy data rows in the body.
When to Use Tables
ā Use tables for:
- Pricing comparison charts
- Schedules and timetables
- Sports standings and scores
- Financial data
- Any data that belongs in rows and columns
ā Never use tables for:
- Page layout (header/sidebar/footer positioning)
- Multi-column text layouts
- Navigation menus
- Anything that CSS Grid or Flexbox handles better