Table Sections: caption, thead, tbody, tfoot and colgroup
Row groups give a table a header, a body and a summary. They are what make long tables printable, scrollable and understandable.
-
HTML Basics
- What is HTML: The Structure Layer of Every Web Page
- HTML Document Structure: DOCTYPE, html, head and body
- Elements, Tags and Attributes: The Vocabulary of HTML
- HTML Comments: Notes That Ship With Your Code
- Block Level and Inline Elements
- Writing and Running Your First HTML Page
- How a Browser Turns Markup Into a Page
- Text and Formatting
- Links and Navigation
- Images and Media
- Lists
- Tables
-
Forms
- Form Structure: form, action and method
- Input Types: Text, Email, Number, Date and the Rest
- Labels: The Most Important Element in a Form
- Checkboxes, Radio Buttons and Grouping
- select, option, optgroup and datalist
- textarea, File Uploads and Hidden Fields
- Buttons: submit, reset and button
- Built In Form Validation
- GET or POST: What Happens When a Form Is Submitted
- Semantic HTML
- HTML5 Features
- Head and Metadata
- HTML with CSS
- HTML with JavaScript
- Accessibility
-
HTML SEO
- How Google Works: Crawling, Indexing and Ranking
- SEO Friendly HTML Structure
- Titles and Descriptions That Earn Clicks
- Headings and Content Structure for Search
- Internal Linking and Anchor Text
- robots.txt and XML Sitemaps
- Canonical URLs and Duplicate Content
- Structured Data and JSON-LD
- Image SEO
- Core Web Vitals and Mobile Friendliness
- DevTools and Debugging
- Editor Productivity
- HTML Best Practices
- HTML Projects
- Advanced Projects
- Practice and Exams
Concept
Beyond rows and cells, a table can be divided into row groups and given a caption and column groups. None of these are decoration; each solves a specific problem that plain rows cannot.
| Element | Purpose |
|---|---|
caption | The name of the table, announced before it |
colgroup / col | Apply width or styling down a whole column |
thead | The header rows |
tbody | The data rows, and there may be several |
tfoot | Summary rows such as totals |
Order in the source
<table>
<caption>...</caption>
<colgroup>...</colgroup>
<thead>...</thead>
<tbody>...</tbody>
<tfoot>...</tfoot>
</table>The caption must be first. tfoot may be written either before or after tbody - older HTML required it before - and browsers render it at the bottom either way. Writing it last is clearer.
caption
<table>
<caption>Fee structure by course and year, 2026 intake</caption>
...
</table>The caption is the accessible name of the table. Screen reader users can list every table on a page; without a caption they see table, table, table. It is one line of markup and it is the difference between a usable page and a guessing game.
Style it, do not hide it. If the design has a heading above the table that says the same thing, use the caption and visually hide the heading rather than the other way round.
thead, tbody and tfoot
<table>
<caption>Monthly rainfall, district stations</caption>
<thead>
<tr>
<th scope="col">Station</th>
<th scope="col">June</th>
<th scope="col">July</th>
<th scope="col">August</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Riverside</th>
<td>184</td><td>312</td><td>276</td>
</tr>
<tr>
<th scope="row">Hill Gate</th>
<td>210</td><td>344</td><td>301</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row">District total</th>
<td>394</td><td>656</td><td>577</td>
</tr>
</tfoot>
</table>What the groups buy you
- Printing. When a long table spans several pages, browsers repeat the
theadat the top of each one. Without it, page two is a wall of unlabelled numbers. - Scrolling. A sticky header is one CSS rule when the header rows are in a
thead. - Sorting and scripting. A script can reorder the rows of
tbodywithout touching the header or the totals. - Styling. Zebra striping, borders and alignment can target the body only.
thead th {
position: sticky;
top: 0;
background: #f1f5f9;
}
tbody tr:nth-child(even) { background: #f8fafc; }
tfoot { font-weight: 600; border-top: 2px solid #94a3b8; }Several tbody elements
A table may have more than one tbody, which is the clean way to group rows into sections.
<table>
<caption>Courses by faculty</caption>
<thead>
<tr><th scope="col">Course</th><th scope="col">Duration</th></tr>
</thead>
<tbody>
<tr><th colspan="2" scope="colgroup">Design</th></tr>
<tr><td>Product design</td><td>3 years</td></tr>
<tr><td>Communication design</td><td>3 years</td></tr>
</tbody>
<tbody>
<tr><th colspan="2" scope="colgroup">Engineering</th></tr>
<tr><td>Civil</td><td>4 years</td></tr>
</tbody>
</table>colgroup and col
Columns do not exist as elements, so colgroup is the only way to address one directly.
<table>
<colgroup>
<col style="width: 40%">
<col span="3" class="numeric">
</colgroup>
...
</table>Only a small set of properties work here - width, background, border and visibility. Text alignment and padding do not inherit through a col, so those still need cell selectors. span lets one col cover several columns.
The practical value is column widths. Setting a width once beats repeating it on every cell.
Important rules
captionmust be the first child oftable.colgroupcomes after the caption and before any row group.- Only one
theadand onetfootper table;tbodymay repeat. - Row groups do not change the visual layout by themselves; they add structure and hooks.
colis a void element.- Browsers insert an implicit
tbodyaround loose rows, so the DOM often has one you did not write.
Common mistakes
- Omitting the caption and putting a heading above the table instead.
- Putting the caption after the rows.
- Skipping
theadon a long table, so printed pages lose their headers. - Expecting
colto apply padding or text alignment. - Using
tfootfor footnotes rather than for summary data. - Wrapping row groups in a
div, which is invalid inside a table.
Best practices
- Caption every table, and write it to be useful when read alone.
- Use
theadandtbodyon every table, even short ones. It costs two lines. - Use
tfootfor totals and averages. - Set column widths through
colgroup. - Make the header sticky on long tables.
- Split logical sections into separate
tbodyelements rather than styling a marker row.
Practice
- Add a caption,
thead,tbodyandtfootto a plain table, then print it to PDF and check whether the header repeats. - Make the header row sticky using two CSS declarations.
- Split a twenty row table into three
tbodygroups with a section heading row in each. - Set the first column to thirty percent width with
colgroup, then try to set its text alignment the same way and explain what happens.