HTML Tables
Present tabular data with semantic, accessible HTML tables
Introduction to HTML Tables
What are HTML Tables?
HTML tables display data in rows and columns. They’re ideal for tabular information like schedules, price lists, and comparison charts.
Tables are built using a combination of elements: <table>, <tr>, <th>, and <td>, often organized into <thead>, <tbody>, and <tfoot>. When used correctly, tables make it easy for users and assistive technologies to scan and compare data.
Basic Table Structure
Example: Simple Table
| Language | Type | Usage |
|---|---|---|
| HTML | Markup | Structure of web pages |
| CSS | Style Sheet | Presentation and layout |
| JavaScript | Programming | Interactivity and logic |
HTML Code:
<table>
<thead>
<tr>
<th>Language</th>
<th>Type</th>
<th>Usage</th>
</tr>
</thead>
<tbody>
<tr>
<td>HTML</td>
<td>Markup</td>
<td>Structure of web pages</td>
</tr>
</tbody>
</table>
All <table> Attributes
In HTML5, the <table> element has one legacy presentational attribute (border) plus all global attributes. Layout, spacing, borders, and width should be handled with CSS—not obsolete HTML attributes.
| Attribute | Values | Description | Example |
|---|---|---|---|
border |
Non-negative integer or empty string | Obsolete. Draws a border around the table and cells. Use CSS border on table, th, and td instead. |
<table border="1"> |
cellpadding |
Pixels (legacy) | Obsolete. Space inside cells. Use CSS padding on th/td. |
<table cellpadding="8"> |
cellspacing |
Pixels (legacy) | Obsolete. Space between cells. Use CSS border-spacing or border-collapse. |
<table cellspacing="0"> |
width |
Length or percentage | Obsolete. Table width. Use CSS width or responsive wrappers. |
<table width="100%"> |
height |
Length or percentage | Obsolete. Table height. Use CSS min-height / height. |
<table height="300"> |
align |
left, center, right |
Obsolete. Horizontal alignment of the table on the page. Use CSS margin or flex/grid on a wrapper. |
<table align="center"> |
bgcolor |
Color name or hex | Obsolete. Background color. Use CSS background-color. |
<table bgcolor="#f8f9fa"> |
frame |
void, above, below, hsides, vsides, lhs, rhs, box, border |
Obsolete. Which outer borders to show. Use CSS borders. | <table frame="box"> |
rules |
none, groups, rows, cols, all |
Obsolete. Which inner grid lines to show. Use CSS border on rows/cells. |
<table rules="rows"> |
summary |
Text | Obsolete. Short description for assistive tech. Use <caption> instead. |
<table summary="Sales by region"> |
id |
Global | Unique identifier for linking, CSS, or JavaScript. | <table id="price-list"> |
class |
Global | CSS framework or custom class names (e.g. Bootstrap table). |
<table class="table table-striped"> |
style |
Global | Inline CSS (prefer external stylesheets). | <table style="width:100%; border-collapse:collapse;"> |
title |
Global | Advisory tooltip on hover. | <table title="Employee directory"> |
lang / dir |
Global | Language and text direction of table content. | <table lang="en" dir="ltr"> |
hidden |
Global | Boolean. Hides the table until the attribute is removed. | <table hidden> |
role |
ARIA | Usually unnecessary—<table> already exposes table semantics. Avoid overriding unless you know ARIA patterns. |
<table role="table"> |
aria-label |
ARIA | Accessible name when no visible <caption> is provided (caption is preferred). |
<table aria-label="Order history"> |
data-* |
Global | Custom data for scripts (sorting, filtering, export). | <table data-source="api/users"> |
Example: Modern table styling with CSS (recommended)
<!-- Avoid border="", cellpadding="", width="" on table -->
<table class="data-table">
<caption>Product Inventory</caption>
<thead>...</thead>
<tbody>...</tbody>
</table>
<style>
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th,
.data-table td {
border: 1px solid #dee2e6;
padding: 0.75rem;
}
</style>
<table> with semantic child elements (caption, thead, tbody, th) so the structure stays meaningful even when presentation comes from CSS.
Uses of Table Semantic Tags
Semantic table tags describe what each part of the table means, not just how it looks. Screen readers use them to announce headers with data cells; browsers use them for default styling and printing; developers use them to write clearer, maintainable markup.
| Tag | Purpose / Use | When to use | Key attributes |
|---|---|---|---|
<table> |
Root container for tabular data | Schedules, price lists, reports, comparisons—any data in rows and columns | class, id, legacy border |
<caption> |
Title or summary of the entire table | Always add one for accessibility; replaces obsolete summary on <table> |
Global attributes; style with CSS caption-side |
<colgroup> |
Groups one or more columns for shared styling | Highlight columns, set widths, or apply background colors to entire columns at once | Global attributes; contains <col> elements |
<col> |
Represents a single column (or span of columns) | Inside <colgroup>; target columns without repeating classes on every cell |
span (number of columns), style, class |
<thead> |
Group of header rows | Column labels at the top; repeated on each printed page in some browsers | Global attributes; contains <tr> with <th> |
<tbody> |
Group of main data rows | Primary table content; a table may have multiple <tbody> sections |
Global attributes; contains <tr> with <td> or <th> |
<tfoot> |
Group of footer/summary rows | Totals, averages, notes—data that summarizes the body | Global attributes; can appear before <tbody> in HTML (renders at bottom) |
<tr> |
One horizontal row of cells | Every row of headers or data; must live inside thead, tbody, or tfoot |
Global attributes |
<th> |
Header cell (row or column label) | Column titles in <thead>; row labels in the first column; total labels in <tfoot> |
scope (col, row, colgroup, rowgroup), colspan, rowspan, abbr, headers |
<td> |
Standard data cell | Actual values in the table body or footer (not labels) | colspan, rowspan, headers (for complex tables) |
Typical semantic structure
Elements must appear in this order inside <table>: caption → colgroup → thead → tfoot → tbody (one or more). Each row (tr) holds cells (th or td).
HTML Code: Semantic table skeleton
<table>
<caption>Monthly Team Performance Report</caption>
<colgroup>
<col class="col-team">
<col span="2" class="col-metrics">
<col class="col-status">
</colgroup>
<thead>
<tr>
<th scope="col">Team</th>
<th scope="col">Tasks Completed</th>
<th scope="col">Pending Tasks</th>
<th scope="col">Status</th>
</tr>
</thead>
<tfoot>
<tr>
<th scope="row">Total</th>
<td>81</td>
<td>15</td>
<td>Overall Good</td>
</tr>
</tfoot>
<tbody>
<tr>
<th scope="row">Frontend</th>
<td>42</td>
<td>6</td>
<td>On Track</td>
</tr>
<tr>
<th scope="row">Backend</th>
<td>39</td>
<td>9</td>
<td>Needs Review</td>
</tr>
</tbody>
</table>
Real-world uses of each semantic tag
<caption>— Names the table (“Q2 Sales by Region”) so users know what they are reading without scanning every cell.<thead>— Holds column headers; screen readers repeat them when navigating down a column.<tbody>— Separates data from headers/footers; useful when JavaScript adds or removes rows dynamically.<tfoot>— Shows totals or summary rows (invoice total, grade average) that apply to all body rows.<th scope="col">— Labels a column (“Price”, “Quantity”).<th scope="row">— Labels a row (“January”, “Product A”) when the first column is a row header.<colgroup>/<col>— Style or width entire columns (e.g. narrow “ID” column, wide “Description” column) without touching every cell.
Example: Full Semantic Table
| Team | Tasks Completed | Pending Tasks | Status |
|---|---|---|---|
| Frontend | 42 | 6 | On Track |
| Backend | 39 | 9 | Needs Review |
| Total | 81 | 15 | Overall Good |
Rowspan and Colspan
rowspan merges cells vertically (across rows), and colspan merges cells horizontally (across columns). These attributes are useful for grouped headings, schedules, and report-style tables.
Example 1: Using colspan for grouped headers
| Student | Marks | Result | ||
|---|---|---|---|---|
| Math | Science | English | ||
| Asha | 88 | 91 | 84 | Pass |
| Rahul | 75 | 80 | 78 | Pass |
Example 2: Using rowspan for timetable grouping
| Day | Time | Subject |
|---|---|---|
| Monday | 10:00 - 11:00 | HTML |
| 11:15 - 12:15 | CSS | |
| Tuesday | 10:00 - 11:00 | JavaScript |
| 11:15 - 12:15 | Bootstrap |
HTML Code:
<tr>
<th rowspan="2">Monday</th>
<td>10:00 - 11:00</td>
<td>HTML</td>
</tr>
<tr>
<td>11:15 - 12:15</td>
<td>CSS</td>
</tr>
<tr>
<th>Student</th>
<th colspan="3">Marks</th>
</tr>
Responsive Tables
On small screens, wide tables can become difficult to read. A simple approach for responsiveness is to wrap your table in a container that allows horizontal scrolling. Bootstrap’s .table-responsive class does this for you.
Example: Responsive Table Container
| Quarter | Revenue | Expenses | Profit |
|---|---|---|---|
| Q1 | $50,000 | $30,000 | $20,000 |
| Q2 | $65,000 | $38,000 | $27,000 |
HTML Code:
<div class="table-responsive">
<table class="table">
...
</table>
</div>
Accessible Tables
For accessibility, always relate header cells and data cells clearly.
- Use
<th scope="col">for column headers. - Use
<th scope="row">for row headers when appropriate. - Provide a
<caption>for context.
Best Practices
- Use tables only for tabular data, not for page layout.
- Keep tables simple; avoid overly complex nesting.
- Use CSS for styling (borders, colors, spacing).
- Ensure tables are readable on smaller screens (consider responsive patterns).