<thead>, <tbody> & <tfoot> Tags in HTML

Quick facts

CategoryNone of their own — they are only allowed inside a <table>
Content modelZero or more <tr> elements
Tag omissionOptional in some cases (browsers even add <tbody> automatically), but writing all start and end tags is best practice
Permitted parentsA <table>: <thead> after any <caption>/<colgroup>, then <tbody>, then <tfoot> last
DOM interfaceHTMLTableSectionElement (all three)

Syntax

<table> <thead> <tr><th>Item</th><th>Price</th></tr> </thead> <tbody> <tr><td>Coffee</td><td>$3</td></tr> </tbody> <tfoot> <tr><th>Total</th><td>$3</td></tr> </tfoot> </table>

Live example

Edit the code below and watch the preview update instantly.

index.html
Preview

Attributes

AttributeDescription
—All three accept only the global attributes. Old attributes like align, valign and char are obsolete; use CSS instead.

Common examples

A complete table with all three sections

Header row in thead, data in tbody, totals in tfoot.

<table> <thead><tr><th>Item</th><th>Price</th></tr></thead> <tbody> <tr><td>Coffee</td><td>$3</td></tr> <tr><td>Cake</td><td>$4</td></tr> </tbody> <tfoot><tr><th>Total</th><td>$7</td></tr></tfoot> </table>

Groups with several tbody elements

Each tbody can be a group, with its own header row.

<table> <thead><tr><th>Day</th><th>Session</th></tr></thead> <tbody> <tr><th colspan="2">Monday</th></tr> <tr><td>9:00</td><td>Welcome</td></tr> </tbody> <tbody> <tr><th colspan="2">Tuesday</th></tr> <tr><td>9:00</td><td>Workshop</td></tr> </tbody> </table>

A sticky header row

The header stays visible while scrolling through a long table.

<style> .scroll { max-height: 300px; overflow: auto; } thead th { position: sticky; top: 0; background: #fff; } </style>

Striped rows in the body only

Target tbody so header and footer rows aren’t striped.

<style> tbody tr:nth-child(even) { background: #f5f5fa; } </style>

Sectioned table vs rows only

A table made only of <tr> rows works, and the browser quietly adds a <tbody> for you. But without <thead>, nothing tells the browser which row is the header.

Explicit sections let header rows repeat on every printed page, make CSS such as sticky headers and striped bodies easy, and keep JavaScript sorting from moving your header row. They cost three extra tags — worth it for any real data table.

With thead/tbody/tfootRows only
Header repeats when printedYesNo
Easy sticky header & stripesYesHarder
Sorting scriptsSafeMay move the header

Common mistakes to avoid

  • Using thead instead of th. The header cells inside thead still need to be <th>.
  • Putting tfoot in the wrong place. Today it should come after tbody (older guides said before; both work, but after reads naturally).
  • More than one thead or tfoot. A table can have many tbody elements, but only one thead and one tfoot.
  • Using sections for layout. Tables are for data; lay out pages with CSS grid or flexbox.

Accessibility & SEO tips

  • Use <th scope="col"> in thead so screen readers announce the column name with each cell.
  • Wrap wide tables in a scrollable container so they don’t make the whole page scroll sideways on phones.
  • Clear, well-structured tables are easy for search engines to understand and can be shown in featured snippets.
  • For very long tables, consider splitting the body into several tbody groups with their own header rows — it makes the data far easier to scan.

Browser support

Supported in every browser, including Chrome, Edge, Firefox, Safari and their mobile versions — it has been part of HTML since the very first versions.

Related tags

Frequently asked questions

Do I have to write <tbody>?
No. If you leave it out, the browser adds a <tbody> around your rows automatically. That matters for CSS: a selector like table > tr won't match, because the rows end up inside the hidden <tbody>. Writing it yourself avoids surprises.
Can a table have more than one <tbody>?
Yes. Several <tbody> elements split the rows into groups, such as one per month or category, which you can style separately. A table can only have one <thead> and one <tfoot>.
How do I keep the table header visible while scrolling?
Wrap the table in a scrolling container and give the header cells thead th { position: sticky; top: 0; } plus a background color, so rows don't show through.
Do <thead>, <tbody> and <tfoot> help accessibility?
They help a bit, but the most important parts are <th> cells with scope and a <caption>. Sections mostly help with styling, printing and scripts.
How do I make the table header repeat on every printed page?
Put the header row in <thead>. Browsers repeat it at the top of each printed page automatically.
Can I style <tbody> rows differently from <thead>?
Yes. Use selectors like thead th { … } and tbody tr:hover { … } to style each section separately.
Does the browser add <tbody> automatically?
Yes. If you write rows directly in <table>, the browser wraps them in a <tbody>, so CSS such as table > tr won't match — use table tr instead.