HTML Tables

โ–ถ Try It Yourself

HTML Tables

1. Introduction

HTML tables are designed for one specific purpose: displaying tabular data โ€” information that has a natural row-and-column structure with relationships between cells and headers. Understanding how to build accessible, well-structured tables โ€” and knowing when not to use them โ€” is an essential HTML skill. This lesson covers the complete table element set and best practices for data tables.

2. Concept

Table Element Hierarchy

Element Role Where It Goes
<table> Root container Wraps everything
<caption> Accessible title First child of table
<thead> Header row group Before tbody
<tbody> Data row group Main table body
<tfoot> Footer row group After tbody
<tr> Table row Inside thead/tbody/tfoot
<th> Header cell Inside tr (usually thead)
<td> Data cell Inside tr (tbody)
Note: Always use <th> for header cells, not <td> with bold styling. The <th> element has semantic meaning โ€” it defines a header for a column or row โ€” and screen readers use it to provide context for data cells.
Tip: Add scope="col" to column headers and scope="row" to row headers. This explicitly links each header to its respective column or row for screen readers.
Warning: Never use tables for page layout. CSS Grid and Flexbox replaced layout tables years ago. Using tables for layout creates inaccessible, brittle pages that are a nightmare to maintain.

3. Basic Example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Tables Demo</title>
  </head>
  <body>
    <table>
      <caption>Q1 2025 Sales by Region</caption>

      <thead>
        <tr>
          <th scope="col">Region</th>
          <th scope="col">January</th>
          <th scope="col">February</th>
          <th scope="col">March</th>
        </tr>
      </thead>

      <tbody>
        <tr>
          <th scope="row">North</th>
          <td>ยฃ12,400</td>
          <td>ยฃ14,100</td>
          <td>ยฃ16,250</td>
        </tr>
        <tr>
          <th scope="row">South</th>
          <td>ยฃ9,800</td>
          <td>ยฃ11,500</td>
          <td>ยฃ13,000</td>
        </tr>
        <tr>
          <th scope="row">East</th>
          <td>ยฃ7,600</td>
          <td>ยฃ8,200</td>
          <td>ยฃ9,750</td>
        </tr>
      </tbody>

      <tfoot>
        <tr>
          <th scope="row">Total</th>
          <td>ยฃ29,800</td>
          <td>ยฃ33,800</td>
          <td>ยฃ39,000</td>
        </tr>
      </tfoot>
    </table>
  </body>
</html>

4. How It Works

Step 1 โ€” caption Provides Context

<caption> is the table’s accessible title. Screen readers announce it before reading cell data, giving users the context they need.

Step 2 โ€” thead, tbody, tfoot Semantics

These grouping elements allow browsers to treat header and footer rows differently (e.g., repeat headers on each printed page). They also make the table structure machine-readable.

Step 3 โ€” scope Attribute on th

scope="col" declares that a header applies to all cells in its column. scope="row" declares it applies to all cells in its row. This is crucial for complex tables where screen readers must associate a cell with the correct header.

Step 4 โ€” colspan and rowspan

Use colspan="2" on a <td> to make it span two columns. Use rowspan="3" to span three rows. Remember to reduce the cell count in the affected rows/columns accordingly.

5. Real-World Example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hosting Plans Comparison</title>
  </head>
  <body>
    <h1>Hosting Plans Comparison</h1>
    <table>
      <caption>Comparison of Starter, Pro, and Enterprise hosting plans</caption>
      <thead>
        <tr>
          <th scope="col">Feature</th>
          <th scope="col">Starter</th>
          <th scope="col">Pro</th>
          <th scope="col">Enterprise</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th scope="row">Monthly Price</th>
          <td>$5</td><td>$20</td><td>$99</td>
        </tr>
        <tr>
          <th scope="row">Storage</th>
          <td>10 GB</td><td>100 GB</td><td>Unlimited</td>
        </tr>
        <tr>
          <th scope="row">Domains</th>
          <td>1</td><td>10</td><td>Unlimited</td>
        </tr>
        <tr>
          <th scope="row">SSL Certificate</th>
          <td>Free</td><td>Free</td><td>Free</td>
        </tr>
        <tr>
          <th scope="row">Support</th>
          <td>Email only</td><td>Email + Chat</td><td>Dedicated Manager</td>
        </tr>
        <tr>
          <th scope="row">Backups</th>
          <td colspan="2">Weekly</td>
          <td>Daily</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

6. Common Mistakes

Using td for header cells

<tr>
  <td><strong>Name</strong></td>
  <td><strong>Age</strong></td>
</tr>

Use th with scope for header cells

<tr>
  <th scope="col">Name</th>
  <th scope="col">Age</th>
</tr>

Missing caption on data tables

<table>
  <thead><tr><th>Item</th><th>Price</th></tr></thead>
</table>

Add a caption for accessibility and context

<table>
  <caption>Product pricing for Q1 2025</caption>
  <thead><tr><th scope="col">Item</th><th scope="col">Price</th></tr></thead>
</table>

7. Try It Yourself

▶ Try It Yourself

8. Quick Reference

Element / Attribute Purpose Notes
<table> Table container Use only for tabular data
<caption> Accessible table title First child of table
<thead> / <tbody> / <tfoot> Row group semantics Improves structure and printing
<th scope="col"> Column header cell Screen reader association
<th scope="row"> Row header cell Screen reader association
colspan Span multiple columns Reduce cell count in that row
rowspan Span multiple rows Reduce cell count in spanned rows

9. Quiz

🧠 Test Yourself

What attribute on <th> explicitly links it to its column or row for screen readers?





โ–ถ Try It Yourself