Table Structure

Difficulty: Intermediate

HTML tables are used to represent tabular data -- information that is logically organized into rows and columns. The main container element is <table>, and within it you build a grid using <tr> (table row) elements that hold either <th> (table header) or <td> (table data) cells. A properly structured table is not just a visual grid; it carries semantic meaning that assistive technologies rely on to help users navigate and understand the data.

The <thead>, <tbody>, and <tfoot> elements are structural grouping elements that divide a table into its logical sections. <thead> wraps the header row(s), <tbody> wraps the main data rows, and <tfoot> wraps summary or footer rows. While browsers will render a table without these elements, including them provides important semantic context, allows independent scrolling of the body while keeping headers fixed, and enables better print formatting where headers and footers can repeat on every page.

The <th> element defines a header cell. By default, browsers render <th> content as bold and centered, while <td> content is normal-weight and left-aligned. More importantly, <th> tells screen readers that a cell is a header, which lets them announce column or row labels as users navigate the table. You should always use <th> for header cells rather than styling <td> elements to look like headers.

The <tfoot> element is often overlooked but is valuable for summary rows such as totals, averages, or footnotes. Interestingly, in the HTML specification the <tfoot> can be placed before <tbody> in the source code (though it renders at the bottom), but in practice most developers place it after <tbody> for readability. Modern browsers handle both orderings correctly.

When building tables, always ensure your rows have a consistent number of cells. Mismatched cell counts cause rendering issues and accessibility problems. Each row within a given section should define the same number of columns (accounting for any colspan attributes), and the total column count should be consistent across <thead>, <tbody>, and <tfoot>.

Code examples

Basic Table Structure

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Age</th>
      <th>City</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alice</td>
      <td>28</td>
      <td>Mumbai</td>
    </tr>
    <tr>
      <td>Bob</td>
      <td>32</td>
      <td>Delhi</td>
    </tr>
  </tbody>
</table>

The most common table pattern: a single header row inside <thead> and data rows inside <tbody>. Each <tr> has the same number of cells to form a consistent grid.

Table with thead, tbody, and tfoot

<table>
  <thead>
    <tr>
      <th>Product</th>
      <th>Price</th>
      <th>Quantity</th>
      <th>Subtotal</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Laptop</td>
      <td>$999</td>
      <td>2</td>
      <td>$1,998</td>
    </tr>
    <tr>
      <td>Mouse</td>
      <td>$25</td>
      <td>5</td>
      <td>$125</td>
    </tr>
    <tr>
      <td>Keyboard</td>
      <td>$75</td>
      <td>3</td>
      <td>$225</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <th colspan="3">Total</th>
      <td>$2,348</td>
    </tr>
  </tfoot>
</table>

The <tfoot> section contains a summary row. Notice how colspan="3" is used on the Total header cell to span across the first three columns, with the total value in the fourth column.

Table with Row Headers

<table>
  <thead>
    <tr>
      <th></th>
      <th>Q1</th>
      <th>Q2</th>
      <th>Q3</th>
      <th>Q4</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>Revenue</th>
      <td>$10K</td>
      <td>$15K</td>
      <td>$12K</td>
      <td>$20K</td>
    </tr>
    <tr>
      <th>Expenses</th>
      <td>$8K</td>
      <td>$9K</td>
      <td>$11K</td>
      <td>$10K</td>
    </tr>
    <tr>
      <th>Profit</th>
      <td>$2K</td>
      <td>$6K</td>
      <td>$1K</td>
      <td>$10K</td>
    </tr>
  </tbody>
</table>

Tables can have both column headers (in <thead>) and row headers (the first cell in each <tbody> row). Using <th> for the row label cells gives screen readers the information they need to announce both the row and column context for any data cell.

Multiple tbody Sections

<table>
  <thead>
    <tr>
      <th>Student</th>
      <th>Subject</th>
      <th>Grade</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alice</td>
      <td>Math</td>
      <td>A</td>
    </tr>
    <tr>
      <td>Alice</td>
      <td>Science</td>
      <td>B+</td>
    </tr>
  </tbody>
  <tbody>
    <tr>
      <td>Bob</td>
      <td>Math</td>
      <td>B</td>
    </tr>
    <tr>
      <td>Bob</td>
      <td>Science</td>
      <td>A-</td>
    </tr>
  </tbody>
</table>

A table can contain multiple <tbody> elements to group related rows. This is useful for styling or scripting different sections independently, such as grouping rows by student or category.

Key points

Concepts covered

table, thead, tbody, tfoot, tr, th, td