Skip to content

<tbody> - Table Body Element

Table Element

The <tbody> element encapsulates one or more rows (<tr>) that constitute the main body of data in a table. It separates the primary table content from headers (<thead>) and footers (<tfoot>), providing semantic structure that helps browsers, assistive technologies, and developers understand the table’s organization.

Result
<table>
<thead>
<!-- header rows -->
</thead>
<tbody>
<tr>
<td>Data 1</td>
<td>Data 2</td>
</tr>
<tr>
<td>Data 3</td>
<td>Data 4</td>
</tr>
</tbody>
<tfoot>
<!-- footer rows -->
</tfoot>
</table>

Within a <table>, elements should appear in this order:

  1. <caption> (optional)
  2. <colgroup> (optional)
  3. <thead> (optional)
  4. <tbody> (one or more)
  5. <tfoot> (optional)

The <tbody> element supports all global HTML attributes.

Result
Result
Result

One powerful feature of <tbody> is the ability to use multiple instances to group related rows:

Result

Visual Grouping

Separate related rows visually with borders or spacing between groups.

JavaScript Manipulation

Easily show/hide entire groups of rows with a single operation.

Styling Flexibility

Apply different styles to different data groups within the same table.

Semantic Meaning

Communicate logical groupings in your data structure.

Result
Result
Result

When the first cell of a row is a header, use <th> with scope="row":

<tbody>
<tr>
<th scope="row">January</th>
<td>$10,000</td>
<td>$12,000</td>
</tr>
<tr>
<th scope="row">February</th>
<td>$11,000</td>
<td>$13,500</td>
</tr>
</tbody>

This helps screen readers announce “January: $10,000” instead of just “$10,000”.

For complex data relationships, use the headers attribute:

<thead>
<tr>
<th id="name" scope="col">Name</th>
<th id="math" scope="col">Math</th>
<th id="science" scope="col">Science</th>
</tr>
</thead>
<tbody>
<tr>
<th id="alice" scope="row">Alice</th>
<td headers="alice math">95</td>
<td headers="alice science">88</td>
</tr>
</tbody>
Result
/* Basic tbody styling */
tbody {
background-color: #fff;
}
/* Alternating row colors */
tbody tr:nth-child(odd) {
background-color: #f9f9f9;
}
tbody tr:nth-child(even) {
background-color: #ffffff;
}
/* Hover effect */
tbody tr:hover {
background-color: #e8f4f8;
cursor: pointer;
}
/* Multiple tbody separation */
tbody + tbody {
border-top: 2px solid #333;
}
/* First and last row in tbody */
tbody tr:first-child {
border-top: 2px solid #ccc;
}
tbody tr:last-child {
border-bottom: 2px solid #ccc;
}
/* Targeting specific tbody */
tbody.highlight {
background-color: #fffacd;
}
  • Always include <tbody> even for simple tables
  • Use multiple <tbody> elements to group related rows
  • Use <th> with scope="row" for row headers
  • Apply consistent styling across tbody elements
  • Keep row count reasonable for performance (paginate if needed)
  • Test with screen readers to ensure proper announcement
  • Don’t mix content types - keep headers in <thead>, data in <tbody>
  • Don’t use deprecated attributes - use CSS for styling
  • Don’t skip semantic structure - always separate header from body
  • Don’t create overly complex structures - keep it simple
  • Don’t forget hover states - improve user interaction
<table>
<caption>Standard Data Table</caption>
<thead>
<tr>
<th scope="col">Column 1</th>
<th scope="col">Column 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data 1</td>
<td>Data 2</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row">Total</th>
<td>Sum</td>
</tr>
</tfoot>
</table>
<table>
<caption>Grouped Data</caption>
<thead>
<tr>
<th scope="col">Item</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
<!-- Group 1 -->
</tbody>
<tbody>
<!-- Group 2 -->
</tbody>
</table>

The <tbody> element is supported in all browsers:

  • ✅ Chrome (all versions)
  • ✅ Firefox (all versions)
  • ✅ Safari (all versions)
  • ✅ Edge (all versions)
  • ✅ Opera (all versions)
  • ✅ Internet Explorer (all versions)