On this page

HTML tables for organizing data

10 min read TextCh. 2 — Content and Multimedia

What are tables for?

HTML tables are designed to present tabular data in an organized way: information that makes sense in rows and columns, such as grades, schedules, price comparisons, or statistics.

It is important to understand that tables should NOT be used to create page layouts. That practice was common in the 2000s, but today we have CSS Flexbox and Grid for layout purposes.

Basic table structure

A complete table has this structure:

<table>
  <caption>Descriptive table title</caption>
  <thead>
    <tr>
      <th>Header 1</th>
      <th>Header 2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Data 1</td>
      <td>Data 2</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td>Summary 1</td>
      <td>Summary 2</td>
    </tr>
  </tfoot>
</table>

Main elements

Element Function
<table> Container for the entire table
<caption> Title or description of the table
<thead> Group of header rows
<tbody> Group of data rows
<tfoot> Group of summary or total rows
<tr> A table row
<th> A header cell (table header)
<td> A data cell (table data)

The caption element

The <caption> provides an accessible title for the table. It must be the first child of <table>:

<table>
  <caption>Monthly plan pricing</caption>
  <!-- ... -->
</table>

Screen readers announce the caption before reading the table, which helps the user decide whether they want to explore the data.

Headers with scope

The scope attribute on <th> indicates whether the header applies to a column or a row:

<!-- Column header -->
<th scope="col">Price</th>

<!-- Row header -->
<th scope="row">Basic plan</th>

This is essential for accessibility. Without scope, screen readers cannot correctly associate each cell with its corresponding header.

Merging cells (colspan and rowspan)

Sometimes you need a cell to span multiple columns or rows:

<table>
  <thead>
    <tr>
      <th scope="col">Product</th>
      <th scope="col" colspan="2">Price</th>
    </tr>
    <tr>
      <th scope="col"></th>
      <th scope="col">Monthly</th>
      <th scope="col">Annual</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Basic</th>
      <td>$10</td>
      <td>$100</td>
    </tr>
    <tr>
      <th scope="row">Premium</th>
      <td>$25</td>
      <td>$250</td>
    </tr>
  </tbody>
</table>
  • colspan="2" — The cell spans 2 columns
  • rowspan="3" — The cell spans 3 rows

Responsive tables

Tables can overflow on small screens. A simple solution is to wrap them in a container with horizontal scroll:

<div style="overflow-x: auto;">
  <table>
    <!-- Table with many columns -->
  </table>
</div>

This allows the user to scroll horizontally only within the table, without affecting the rest of the page.

Best practices for tables

  1. Always use <caption> to describe the table's content
  2. Use <thead>, <tbody>, and <tfoot> to structure the table semantically
  3. Add scope to <th> elements to improve accessibility
  4. Do not use tables for layout — use CSS Grid or Flexbox
  5. Keep tables simple — if you need more than 2 levels of headers, consider splitting into smaller tables

Practice

  1. Build a schedule table: Create a table with <caption>, <thead>, <tbody>, and <tfoot> that displays a weekly class schedule. Use scope="col" and scope="row" on the headers.
  2. Merge cells: Modify your table so that at least one cell uses colspan and another uses rowspan, simulating a schedule where a class spans two hours or two days.
  3. Make it responsive: Wrap your table in a container with overflow-x: auto and verify that it scrolls horizontally on small screens.

In the next lesson, we will dive deeper into semantic HTML and its impact on accessibility and SEO.

Do not use tables for layout
Tables are exclusively for tabular data. Never use them to create layouts or page structures. CSS Flexbox and CSS Grid exist for that purpose.
Table accessibility
Use the scope attribute on th elements to indicate whether the header applies to a column (col) or a row (row). This helps screen readers correctly associate cells with their headers.
html
<table>
  <caption>First quarter grades</caption>
  <thead>
    <tr>
      <th scope="col">Student</th>
      <th scope="col">Math</th>
      <th scope="col">Science</th>
      <th scope="col">Average</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Anna Lopez</th>
      <td>95</td>
      <td>88</td>
      <td>91.5</td>
    </tr>
    <tr>
      <th scope="row">Carlos Ruiz</th>
      <td>78</td>
      <td>92</td>
      <td>85</td>
    </tr>
    <tr>
      <th scope="row">Maria Torres</th>
      <td>86</td>
      <td>90</td>
      <td>88</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <th scope="row">Overall average</th>
      <td>86.3</td>
      <td>90</td>
      <td>88.2</td>
    </tr>
  </tfoot>
</table>