HTMLSections (<thead>, <tbody>, <tfoot>)

Sections (<thead>, <tbody>, <tfoot>)

Large tables benefit from being split into three logical sections: a header, a body, and an optional footer. <thead>, <tbody>, and <tfoot> group rows semantically, give you clean CSS styling hooks, and — critically for printed or paginated tables — let the header repeat on every page.

Basic Structure

HTML
<table>
  <thead>
    <tr>
      <th scope="col">Order #</th>
      <th scope="col">Customer</th>
      <th scope="col">Total</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1001</td>
      <td>Maria Chen</td>
      <td>$84.20</td>
    </tr>
    <tr>
      <td>1002</td>
      <td>James Okafor</td>
      <td>$32.10</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <th scope="row" colspan="2">Total</th>
      <td>$116.30</td>
    </tr>
  </tfoot>
</table>

Section

Purpose

<thead>

Column header row(s) — appears once, describes the columns

<tbody>

The actual data rows — a table can have multiple <tbody> groups

<tfoot>

Summary/total row(s) — totals, averages, or footnotes

Note
Each of these sections is optional individually, but if you use any of them, the browser expects a specific order in the DOM: <thead>, <tbody>, then <tfoot>. Interestingly the HTML spec also allows <tfoot> to appear right after <thead> (before the body) in markup, and browsers still render it visually last.
Multiple <tbody> Groups

A single table can contain several <tbody> elements. This is useful for visually and semantically grouping related rows — for example, splitting a product catalog table by category, while keeping it all one table (one set of column headers).

HTML
<table>
  <thead>
    <tr>
      <th scope="col">Product</th>
      <th scope="col">Price</th>
    </tr>
  </thead>

  <tbody>
    <tr><th scope="rowgroup" colspan="2">Laptops</th></tr>
    <tr><td>UltraBook 14"</td><td>$1,299</td></tr>
    <tr><td>ProBook 16"</td><td>$1,899</td></tr>
  </tbody>

  <tbody>
    <tr><th scope="rowgroup" colspan="2">Monitors</th></tr>
    <tr><td>27" 4K Display</td><td>$449</td></tr>
    <tr><td>34" Ultrawide</td><td>$799</td></tr>
  </tbody>
</table>
Tip
Splitting rows into multiple <tbody> groups makes it easy to target a specific group with CSS (tbody:nth-of-type(2)) or JavaScript, without adding extra wrapper markup that would break the table structure.
CSS Styling Hooks

Because <thead>, <tbody>, and <tfoot> are real elements, they're natural, low-specificity CSS selectors — no extra classes needed for common patterns like sticky headers or a visually distinct footer.

CSS
thead {
  background-color: #1976d2;
  color: white;
}

thead th {
  position: sticky;
  top: 0; /* header stays visible while scrolling a tall table */
}

tfoot {
  font-weight: bold;
  border-top: 2px solid #333;
}

tbody tr:hover {
  background-color: #f0f4ff;
}
Printing Implications: Repeating <thead>

When a long table spans multiple printed pages, browsers automatically repeat the{' '} <thead> content at the top of each new page — but only if you actually used{' '} <thead>. A table built only from a bare first <tr> (no <thead> wrapper) will not repeat, leaving later pages with unlabeled columns.

CSS
@media print {
  thead {
    display: table-header-group; /* ensures repeat-on-each-page behavior */
  }
  tfoot {
    display: table-footer-group;
  }
}
Warning
If a report or invoice table needs to print correctly across multiple pages, always wrap the header row in <thead>. Skipping it is one of the most common reasons a "print this table" feature looks broken on page 2 onward.
Key Takeaways
  1. thead, tbody, and tfoot group table rows into header, body, and footer sections.

  2. DOM order is thead, then tbody, then tfoot — the browser renders tfoot last visually regardless of where it appears relative to tbody in markup.

  3. A table can contain multiple tbody groups to organize related rows without extra wrapper markup.

  4. These sections are natural CSS selectors for sticky headers, alternating row styles, and footer emphasis.

  5. Using <thead> is what makes column headers repeat automatically across printed pages.