HTML Tables Explained - Build Professional Data Tables
Sometimes you need to display data in an organized way with rows and columns - like a price comparison table, a grades table, or a product list. HTML provides a set of tags designed exactly for this purpose.
Core Table Tags - The Big Four
Every HTML table is built from four core tags working together:
| Tag | Full Name | Purpose |
|---|---|---|
<table> |
Table | Main container - wraps the entire table |
<tr> |
Table Row | Horizontal row inside the table |
<th> |
Table Header | Header cell - bold and centered by default |
<td> |
Table Data | Regular data cell |
The Simplest Possible Table
<table>
<tr>
<th>Name</th>
<th>Age</th>
<th>City</th>
</tr>
<tr>
<td>Ahmed</td>
<td>25</td>
<td>Riyadh</td>
</tr>
<tr>
<td>Sara</td>
<td>30</td>
<td>Cairo</td>
</tr>
</table>
Notice the structure: <table> contains <tr>, and each <tr> contains <th> or <td>.
The first row usually uses <th> for headings, and the rest use <td> for data.
Remember: Without CSS the table appears without visible borders. Add borders with CSS: table, th, td { border: 1px solid black; }
Semantic Table Sections - thead, tbody, tfoot
For large and professional tables, the table is split into three semantic sections. This improves code readability and helps search engines and screen readers.
| Tag | Purpose |
|---|---|
<thead> |
Table head - contains header rows |
<tbody> |
Table body - contains the main data |
<tfoot> |
Table footer - contains summaries and totals |
<table>
<!-- Table head -->
<thead>
<tr>
<th>Product</th>
<th>Quantity</th>
<th>Price</th>
</tr>
</thead>
<!-- Table body -->
<tbody>
<tr>
<td>HTML Book</td>
<td>2</td>
<td>$50</td>
</tr>
<tr>
<td>CSS Course</td>
<td>1</td>
<td>$30</td>
</tr>
</tbody>
<!-- Table footer -->
<tfoot>
<tr>
<td colspan="2">Total</td>
<td>$130</td>
</tr>
</tfoot>
</table>
Merging Cells - colspan and rowspan
Sometimes you need one cell to span multiple columns or rows.
This is the role of colspan and rowspan.
Merging Columns - colspan
Makes a cell span a number of columns. The number equals how many columns to merge.
<table>
<tr>
<!-- This cell spans two columns -->
<th colspan="2">Personal Information</th>
</tr>
<tr>
<td>First Name</td>
<td>Last Name</td>
</tr>
</table>
Merging Rows - rowspan
Makes a cell span multiple rows.
<table>
<tr>
<!-- This cell spans two rows -->
<td rowspan="2">Group A</td>
<td>Student: Ahmed</td>
</tr>
<tr>
<!-- Do not put a cell here - the previous cell occupies this space -->
<td>Student: Sara</td>
</tr>
</table>
<td> from the second row in the same column - because the merged cell already occupies that space.
Table Caption - <caption>
You can add a title for the table that appears above it automatically:
<table>
<caption>Student Grades - Semester 1 2025</caption>
<thead>
<tr>
<th>Student</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<tr>
<td>Ahmed</td>
<td>95</td>
</tr>
</tbody>
</table>
Full Practical Example - Pricing Plan Comparison
<table>
<caption>Plan Comparison</caption>
<thead>
<tr>
<th>Feature</th>
<th>Free</th>
<th>Paid</th>
</tr>
</thead>
<tbody>
<tr>
<td>Core lessons</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Advanced lessons</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Certificate</td>
<td>No</td>
<td>Yes</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2"></td>
<td><strong>$29/month</strong></td>
</tr>
</tfoot>
</table>
Frequently Asked Questions - FAQ
What is the difference between <th> and <td>?
<th> is for headings - its text is bold and centered by default, and it tells search engines this is a header cell.
<td> is for regular data - its text is normal and alignment depends on the default text direction.
Are <thead> and <tbody> required?
No, the table works without them. But they improve code readability, help apply separate CSS styles to each section, and are important for SEO and accessibility.
Can I put images or links inside table cells?
Yes, <td> can contain any HTML content: text, images, links, even nested tables.
How do I make a table responsive on mobile?
Tables are naturally hard on mobile. The most common solution is to add overflow-x: auto to a wrapper element:
<div style="overflow-x: auto;"><table>...</table></div>