Tables are used to represent structured data where information has meaningful relationships between rows and columns.
Examples:
The most important rule is:
Use HTML tables for tabular data, not for creating webpage layouts.
Imagine this data:
| Name | Age | Country |
|---|---|---|
| Anubhav | 30 | India |
| Rahul | 28 | India |
| John | 32 | USA |
The data has relationships:
Name ↔ Age ↔ CountryHTML can represent those relationships semantically.
<table>#The simplest table:
<table>
<tr>
<td>Anubhav</td>
<td>30</td>
<td>India</td>
</tr>
<tr>
<td>Rahul</td>
<td>28</td>
<td>India</td>
</tr>
</table>The result is conceptually:
Anubhav 30 India
Rahul 28 IndiaBut this isn't a properly structured data table yet.
We need to understand the individual elements.
<table>#The outer container is:
<table>
...
</table>It tells the browser:
This content represents tabular data.
<tr> — Table Row#tr means:
Table Row
Example:
<tr>
...
</tr>A table consists of rows.
<table>
│
├── <tr>
├── <tr>
└── <tr><td> — Table Data Cell#td means:
Table Data
Example:
<td>Anubhav</td>A row can contain multiple data cells:
<tr>
<td>Anubhav</td>
<td>30</td>
<td>India</td>
</tr>Conceptually:
┌─────────┬─────┬───────┐
│ Anubhav │ 30 │ India │
└─────────┴─────┴───────┘<th> — Table Header Cell#th represents a header cell.
Example:
<tr>
<th>Name</th>
<th>Age</th>
<th>Country</th>
</tr>Now the browser and assistive technologies know that:
Name → header for a column
Age → header for a column
Country → header for a columnA basic table should therefore look like:
<table>
<tr>
<th>Name</th>
<th>Age</th>
<th>Country</th>
</tr>
<tr>
<td>Anubhav</td>
<td>30</td>
<td>India</td>
</tr>
<tr>
<td>Rahul</td>
<td>28</td>
<td>India</td>
</tr>
</table>Conceptually:
┌──────────┬─────┬─────────┐
│ Name │ Age │ Country │ ← Header
├──────────┼─────┼─────────┤
│ Anubhav │ 30 │ India │
├──────────┼─────┼─────────┤
│ Rahul │ 28 │ India │
└──────────┴─────┴─────────┘<thead>#For better structure, group header rows inside:
<thead>Example:
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Country</th>
</tr>
</thead>
</table><tbody>#The main body of the table goes inside:
<tbody>Example:
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Country</th>
</tr>
</thead>
<tbody>
<tr>
<td>Anubhav</td>
<td>30</td>
<td>India</td>
</tr>
<tr>
<td>Rahul</td>
<td>28</td>
<td>India</td>
</tr>
</tbody>
</table><tfoot>#<tfoot> contains summary/footer rows.
For example, an invoice:
<table>
<thead>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Keyboard</td>
<td>₹2,000</td>
</tr>
<tr>
<td>Mouse</td>
<td>₹1,000</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>Total</th>
<th>₹3,000</th>
</tr>
</tfoot>
</table>Structure:
<table>
│
├── <thead>
│
├── <tbody>
│
└── <tfoot><caption>#A table can have a caption:
<table>
<caption>
Employee Information
</caption>
...
</table>This tells users what the table represents.
Example:
<table>
<caption>
Employee salaries for 2026
</caption>
...
</table>A caption is especially useful for accessibility and context.
<table>
<caption>
Employee Information
</caption>
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th>Experience</th>
</tr>
</thead>
<tbody>
<tr>
<td>Anubhav</td>
<td>Software Engineer</td>
<td>5 Years</td>
</tr>
<tr>
<td>Rahul</td>
<td>Backend Developer</td>
<td>4 Years</td>
</tr>
</tbody>
</table>Think of the table as:
Table
│
├── Caption
│
├── Header
│ ├── Name
│ ├── Role
│ └── Experience
│
└── Data
├── Anubhav
├── Software Engineer
└── 5 YearsThe browser and assistive technology can understand these relationships.
scope#For accessible tables, <th> can use the scope attribute.
For column headers:
<th scope="col">Name</th>
<th scope="col">Age</th>
<th scope="col">Country</th>This communicates:
This header applies to the cells in its column.
Suppose you have:
| Product | January | February |
|---|---|---|
| Laptop | ₹80,000 | ₹75,000 |
| Phone | ₹40,000 | ₹38,000 |
The product names can be row headers:
<table>
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col">January</th>
<th scope="col">February</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Laptop</th>
<td>₹80,000</td>
<td>₹75,000</td>
</tr>
<tr>
<th scope="row">Phone</th>
<td>₹40,000</td>
<td>₹38,000</td>
</tr>
</tbody>
</table>Notice:
<th scope="row">rather than:
<td>because "Laptop" and "Phone" identify the rows.
scope Matters#Imagine a screen reader navigating:
Laptop → January → ₹80,000The semantic relationships help the user understand:
₹80,000
↓
Laptop
↓
JanuaryWithout appropriate table semantics, complex tables can become difficult to understand.
colspan#colspan allows a cell to span multiple columns.
Example:
<tr>
<th colspan="3">
Employee Information
</th>
</tr>If the table has three columns:
┌───────────────────────────────┐
│ Employee Information │
├──────────┬─────┬─────────────┤
│ Name │ Age │ Department │
└──────────┴─────┴─────────────┘The header occupies all three columns.
rowspan#rowspan allows a cell to span multiple rows.
Example:
<tr>
<th rowspan="2">
Frontend
</th>
<td>HTML</td>
</tr>
<tr>
<td>CSS</td>
</tr>Conceptually:
┌───────────┬───────┐
│ │ HTML │
│ Frontend ├───────┤
│ │ CSS │
└───────────┴───────┘rowspan and colspan#You can combine them when the data structure requires it.
Example:
<table>
<tr>
<th rowspan="2">Product</th>
<th colspan="2">
Sales
</th>
</tr>
<tr>
<th>January</th>
<th>February</th>
</tr>
</table>Structure:
┌─────────┬─────────────────────┐
│ Product │ Sales │
│ ├──────────┬──────────┤
│ │ January │ February │
└─────────┴──────────┴──────────┘For more complicated tables, you may need:
scopeidheadersFor example:
<th id="name">Name</th>
<th id="salary">Salary</th>Then a cell can reference headers:
<td headers="name">Anubhav</td>
<td headers="salary">₹30 LPA</td>This is useful for complex table relationships.
For ordinary tables, correct use of <th> and scope is usually sufficient.
This is extremely important.
Developers historically used tables to position webpage elements:
<table>
<tr>
<td>Logo</td>
<td>Navigation</td>
</tr>
<tr>
<td>Main Content</td>
<td>Sidebar</td>
</tr>
</table>Don't do this.
Use semantic HTML:
<header>
...
</header>
<main>
...
</main>
<aside>
...
</aside>and CSS for layout.
HTML:
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>Anubhav</td>
<td>30</td>
</tr>
</tbody>
</table>CSS controls:
For example:
table {
border-collapse: collapse;
}We'll learn this in the CSS course.
border Attribute#You may see old HTML like:
<table border="1">Avoid this in modern HTML.
Presentation belongs in CSS.
Use:
table,
th,
td {
border: 1px solid black;
}Don't use old attributes such as:
<td align="center">Modern HTML separates structure and presentation.
Use CSS:
td {
text-align: center;
}Tables can be difficult to display on small screens.
For example:
Desktop
┌──────┬──────┬──────┬──────┬──────┐
│ Name │ Age │ City │ Role │ Salary│
└──────┴──────┴──────┴──────┴──────┘On mobile, the table might be wider than the viewport.
One common approach is to place the table inside a horizontally scrollable container:
<div class="table-container">
<table>
...
</table>
</div>CSS can then allow horizontal scrolling.
The exact responsive design depends on the data.
Sometimes developers completely transform a table into cards on mobile.
That can work visually, but be careful not to destroy the underlying data relationships and accessibility.
For genuinely tabular information, preserving table semantics is usually important.
Consider:
<table>
<caption>
Monthly Revenue — 2026
</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Revenue</th>
<th scope="col">Expenses</th>
<th scope="col">Profit</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">January</th>
<td>₹10,00,000</td>
<td>₹7,00,000</td>
<td>₹3,00,000</td>
</tr>
<tr>
<th scope="row">February</th>
<td>₹12,00,000</td>
<td>₹7,50,000</td>
<td>₹4,50,000</td>
</tr>
</tbody>
</table>This is a proper use of a table because the information has row/column relationships.
<table>
<caption>
Student Examination Results
</caption>
<thead>
<tr>
<th scope="col">Student</th>
<th scope="col">HTML</th>
<th scope="col">CSS</th>
<th scope="col">JavaScript</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Anubhav</th>
<td>95</td>
<td>90</td>
<td>92</td>
</tr>
<tr>
<th scope="row">Rahul</th>
<td>88</td>
<td>91</td>
<td>85</td>
</tr>
</tbody>
</table>A pricing comparison is another common example.
<table>
<caption>
Hosting Plans
</caption>
<thead>
<tr>
<th scope="col">Feature</th>
<th scope="col">Basic</th>
<th scope="col">Pro</th>
<th scope="col">Enterprise</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Storage</th>
<td>10 GB</td>
<td>100 GB</td>
<td>1 TB</td>
</tr>
<tr>
<th scope="row">Users</th>
<td>5</td>
<td>50</td>
<td>Unlimited</td>
</tr>
</tbody>
</table>Let's create a sales report:
<table>
<caption>
Sales Report
</caption>
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col">Units</th>
<th scope="col">Revenue</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Laptop</th>
<td>20</td>
<td>₹16,00,000</td>
</tr>
<tr>
<th scope="row">Phone</th>
<td>30</td>
<td>₹12,00,000</td>
</tr>
<tr>
<th scope="row">Tablet</th>
<td>10</td>
<td>₹3,00,000</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row">Total</th>
<td>60</td>
<td>₹31,00,000</td>
</tr>
</tfoot>
</table>Think about a table like this:
TABLE
│
┌─────────┼─────────┐
↓ ↓ ↓
THEAD TBODY TFOOT
│ │ │
↓ ↓ ↓
Rows Rows Rows
│ │ │
↓ ↓ ↓
Cells Cells CellsAnd:
Header cell → <th>
Data cell → <td>Suppose you have:
HTML
CSS
JavaScriptThat's a list:
<ul>
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
</ul>But:
Technology Years Experience
HTML 5
CSS 5
JavaScript 4has meaningful columns.
Use:
<table>Consider:
Processor
Intel i9
RAM
32 GB
Storage
2 TBA description list can be appropriate:
<dl>
<dt>Processor</dt>
<dd>Intel i9</dd>
<dt>RAM</dt>
<dd>32 GB</dd>
<dt>Storage</dt>
<dd>2 TB</dd>
</dl>But if you're comparing multiple products:
Laptop A Laptop B
Processor i7 i9
RAM 16 GB 32 GB
Storage 1 TB 2 TBa table is much more appropriate.
For a table, consider:
✓ Caption when useful
✓ <th> for headers
✓ scope="col" for column headers
✓ scope="row" for row headers
✓ <thead>
✓ <tbody>
✓ <tfoot> where appropriate
✓ Proper relationships in complex tables
✓ Don't use tables for layout<table>
<tr>
<td>Name</td>
<td>Age</td>
</tr>
</table>If these are headers, use <th>.
<table>
<tr>
<td>Header</td>
</tr>
</table>Use semantic layout elements and CSS.
<div> for tabular data#<div>
Anubhav | 30 | India
</div>If the data has real row/column relationships, use a table.
Avoid:
<table border="1">Use CSS.
| Element | Purpose |
|---|---|
<table> |
Table |
<caption> |
Table title/caption |
<thead> |
Header section |
<tbody> |
Main data |
<tfoot> |
Footer/summary |
<tr> |
Table row |
<th> |
Header cell |
<td> |
Data cell |
scope="col" |
Column header relationship |
scope="row" |
Row header relationship |
colspan |
Span columns |
rowspan |
Span rows |
Create:
Your page should contain:
Employee Management
┌──────────┬──────────────┬──────────┬────────────┐
│ Name │ Department │ Exp. │ Salary │
├──────────┼──────────────┼──────────┼────────────┤
│ Anubhav │ Engineering │ 5 years │ ₹26.5 LPA │
│ Rahul │ Engineering │ 4 years │ ₹20 LPA │
│ Priya │ Marketing │ 3 years │ ₹15 LPA │
└──────────┴──────────────┴──────────┴────────────┘Requirements:
<table><caption><thead><tbody><tfoot><th><td>scope="col"scope="row"Then create another table:
Use:
Month
Product
Units
RevenueCreate at least five rows.
Add a total in <tfoot>.
<table>?<tr>?<td>?<th>?<th> and <td>?<thead>?<tbody>?<tfoot>?<caption>?scope="col"?scope="row"?colspan?rowspan?When you have structured information, ask:
What is the relationship between the data?
│
┌────────┼─────────┐
↓ ↓ ↓
Collection Term + Rows &
meaning Columns
│ │ │
<ul> <dl> <table>
<ol>The most important rule:
If the data makes sense because of its rows and columns, use a table.
Don't use a table simply because you want something to look like a grid.
We'll enter one of the most important parts of HTML: forms and user input. You'll learn <form>, <input>, <label>, name, value, action, method, GET vs POST, text fields, passwords, email, numbers, dates, URLs, telephone inputs, buttons, and how browsers actually submit form data.
Mark it as complete to track your mastery.