In short
Use colspan and rowspan to merge table cells, the headers attribute for complex tables, and simple techniques to keep HTML tables accessible and readable on mobile. How to merge cells with colspan Sometimes one cell needs to cover more than one column.
Use colspan and rowspan to merge table cells, the headers attribute for complex tables, and simple techniques to keep HTML tables accessible and readable on mobile.
How to merge cells with colspan
Sometimes one cell needs to cover more than one column. For example, a "Total" label that spans two columns. The colspan attribute does this. Its value is the number of columns the cell should cover.
Think of it like joining two seats on a bench into one long seat. The row still has the same total width, so you remove the cells that were "eaten" by the wide one.
<table>
<caption>Your order</caption>
<thead>
<tr>
<th scope="col">Item</th>
<th scope="col">Quantity</th>
<th scope="col">Price</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Coffee beans (500 g)</th>
<td>2</td>
<td>£16.00</td>
</tr>
<tr>
<th scope="row">Ceramic mug</th>
<td>1</td>
<td>£12.00</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row" colspan="2">Total</th>
<td>£28.00</td>
</tr>
</tfoot>
</table>
The "Total" cell now stretches across the "Item" and "Quantity" columns. The footer row has only two cells in the code, but it still fills all three columns.
How to merge cells with rowspan
rowspan works the same way but down instead of across. A cell with rowspan="2" covers its own row and the row below. In the next row, you leave out the cell that would be under it.
<table>
<caption>Conference schedule, Saturday</caption>
<thead>
<tr>
<th scope="col">Time</th>
<th scope="col">Room A</th>
<th scope="col">Room B</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">09:00</th>
<td rowspan="2">HTML Workshop (2 hours)</td>
<td>Intro to Design</td>
</tr>
<tr>
<th scope="row">10:00</th>
<td>Colour Theory</td>
</tr>
<tr>
<th scope="row">11:00</th>
<td colspan="2">Coffee break for everyone</td>
</tr>
</tbody>
</table>
The HTML Workshop cell in Room A is two rows tall, because it lasts two hours. The 10:00 row has only one <td>, for Room B. At 11:00, the coffee break stretches across both rooms with colspan="2".
Common mistake: Forgetting to remove the extra cells. If you add
colspan="2"but keep all the original cells, the row becomes too wide and the table looks broken. Count the columns in every row: they must always add up to the same number.
The headers attribute for complex tables
For simple tables, scope is enough. For very complex tables with several levels of headers, you can connect cells to headers directly. Give each <th> an id, then list those ids in the headers attribute of a <td>, separated by spaces.
<th id="q1">Quarter 1</th>
<th id="sales">Sales</th>
<td headers="q1 sales">£40,000</td>
You will rarely need this. If your table needs it, ask yourself first if splitting it into two simpler tables would be clearer.
Don't use tables for page layout
Years ago, developers used tables to place things side by side, like a sidebar next to content. Please don't do this today. Layout tables confuse screen readers (they announce "table, 3 columns" for something that isn't data), are hard to change, and don't adapt well to phones. Use semantic HTML for structure and CSS Flexbox or Grid for layout. You will learn these in the CSS course.
Keeping tables readable on mobile
Wide tables can be too big for a phone screen. The simplest and most reliable fix is to wrap the table in a container that can scroll sideways. In HTML you only add the wrapper; CSS will give it overflow-x: auto later.
<div class="table-scroll" role="region" aria-labelledby="prices-caption" tabindex="0">
<table>
<caption id="prices-caption">Phone plans compared</caption>
<thead>
<tr>
<th scope="col">Plan</th>
<th scope="col">Data</th>
<th scope="col">Minutes</th>
<th scope="col">Texts</th>
<th scope="col">Price</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Starter</th>
<td>5 GB</td>
<td>500</td>
<td>Unlimited</td>
<td>£8</td>
</tr>
<tr>
<th scope="row">Plus</th>
<td>50 GB</td>
<td>Unlimited</td>
<td>Unlimited</td>
<td>£15</td>
</tr>
</tbody>
</table>
</div>
The <div> is the scroll box. role="region" and aria-labelledby give it a name for screen readers, and tabindex="0" lets keyboard users focus the box and scroll it with arrow keys. Once CSS adds overflow-x: auto to .table-scroll, the table scrolls sideways on small screens instead of breaking the page.
Tip: Keep tables simple. Fewer columns, short header text and clear captions help on every screen size.
Practice exercise
Make a school timetable for one day with three time slots and two classes. Use rowspan for a lesson that lasts two slots, and colspan for a lunch break that covers both classes. Wrap the table in a scroll container like the last example.
Hint: After you add a rowspan, remove one cell from the row below. After you add a colspan, remove cells from the same row.
Key takeaways
colspanmerges cells across columns;rowspanmerges cells down rows.- Every row must add up to the same number of columns, so remove the cells you merged.
- Use
scopefor simple tables andheadersonly for complex ones. - Never use tables for layout, and wrap wide tables in a scrollable container for mobile.