HTML Colspan and Rowspan

Complex Table Layouts

colspan and rowspan are the two attributes that turn a basic grid into a table that can actually represent complex data relationships - a header that labels three columns at once, a time slot that runs for two hours, a category label that applies to several rows of data underneath it. I avoided these for a while because they seemed complicated and I kept miscounting cells and breaking the layout in ways that looked strange and took forever to debug, but once you understand the cell-counting rule they're pretty manageable. The key insight is that a colspan or rowspan cell takes up space that would otherwise be occupied by other cells, so those other cells must be omitted from the HTML.

Key Attributes

  • colspan - Makes a cell stretch horizontally across multiple columns. colspan="3" means the cell takes up the space of three columns side by side.
  • rowspan - Makes a cell stretch vertically down multiple rows. rowspan="3" means the cell takes up the space of three rows stacked.
  • Combined - Both attributes can be on the same cell to span multiple columns and multiple rows simultaneously.

What are Colspan and Rowspan?

Both attributes go on td or th elements and take an integer value representing how many columns or rows that cell should cover. The default value for both is 1, meaning the cell occupies exactly one cell's worth of space, which is normal table behavior. Setting colspan to 2 means that cell now occupies two columns worth of space horizontally, and the row it lives in needs one fewer td as a result.

html
1<!-- Syntax -->
2<td colspan="number">Content</td>
3<td rowspan="number">Content</td>
4
5<!-- Simple colspan example -->
6<table>
7  <tr>
8    <td colspan="3">This cell spans three columns</td>
9  </tr>
10  <tr>
11    <td>Col 1</td>
12    <td>Col 2</td>
13    <td>Col 3</td>
14  </tr>
15</table>

Attribute Definitions

  • colspan - An integer specifying how many columns wide this cell should be. The cells it replaces must be removed from the HTML.
  • rowspan - An integer specifying how many rows tall this cell should be. The cells it replaces in subsequent rows must be removed from those rows.

Colspan: Spanning Cells Across Columns

Colspan is the more intuitive of the two because everything stays on the same row - a cell with colspan="2" just takes up two columns instead of one, and the row it's on needs one fewer cell to make the totals work out. The most common use is group headers: a single header cell that labels two or three data columns that fall under the same category.

html
1<table border="1">
2  <tr>
3    <th colspan="2">Employee Details</th>
4  </tr>
5  <tr>
6    <td>John Doe</td>
7    <td>johndoe@example.com</td>
8  </tr>
9</table>

Practical Use Cases

  • Group headers - A single header that labels multiple related columns - like 'Contact Information' above separate Name and Email columns.
  • Summary rows - A totals or notes row that spans all columns in a table, like a 'Grand Total' row at the bottom.
  • Section dividers - A spanning cell used as a visual separator between sections in a long table.

Rowspan: Spanning Cells Across Rows

Rowspan is the trickier one because the effect spans multiple rows, which means you have to remember to remove the corresponding cells from the rows below. A cell with rowspan="3" appears once in your HTML in the first row it occupies, and the next two rows each have one fewer td than they otherwise would because that space is already taken by the spanning cell. Getting the cell count wrong in the rows below is the most common rowspan mistake.

html
1<table border="1">
2  <tr>
3    <th rowspan="3">Team Members</th>
4    <td>John Doe</td>
5  </tr>
6  <tr>
7    <!-- No cell here - rowspan above covers this space -->
8    <td>Jane Smith</td>
9  </tr>
10  <tr>
11    <!-- No cell here either -->
12    <td>Mike Johnson</td>
13  </tr>
14</table>

Practical Use Cases

  • Category labels - A department or category label that spans multiple rows of data belonging to that category.
  • Time blocks - A class or event that runs for two hours in a schedule table, spanning two time-slot rows.
  • Merged data cells - A value that applies to multiple rows, like a date that covers several entries.

Combining Colspan and Rowspan

You can use both attributes in the same table - and on the same cell - for layouts where data has both horizontal and vertical grouping. This is where drawing the table on paper first becomes genuinely useful rather than optional, because keeping track of which cells occupy which positions gets complicated fast. The class schedule example below uses both: a lunch break row that spans all day columns, and lab sessions that span two time slots.

html
1<table border="1">
2  <tr>
3    <th colspan="4">Company Leadership</th>
4  </tr>
5  <tr>
6    <th>Department</th>
7    <th>Manager</th>
8    <th colspan="2">Team Leads</th>
9  </tr>
10  <tr>
11    <td rowspan="2">Development</td>
12    <td>Sarah Kim</td>
13    <td>Frontend</td>
14    <td>Backend</td>
15  </tr>
16  <tr>
17    <!-- rowspan above covers col 1 -->
18    <td></td>
19    <td>Alex Chen</td>
20    <td>Maria Lopez</td>
21  </tr>
22  <tr>
23    <td>Marketing</td>
24    <td>David Wilson</td>
25    <td colspan="2">No team leads</td>
26  </tr>
27</table>

Complex Layout Tips

  • Draw it first - Sketch the table on paper or in a grid editor before writing HTML. Mark which cells span and which cells are removed as a result.
  • Count cells per row - Each row's cell count plus the colspan values of cells starting in that row must equal the total column count. Same principle applies vertically for rowspan.

Best Practices

The most useful habit with colspan and rowspan is planning before coding - even a rough sketch of the table layout on paper or a whiteboard prevents the most common errors. Beyond that: add scope attributes to your th elements in complex tables so screen readers can match cells to their headers, keep the spanning as simple as the data allows, and test the table on narrow screens since wide tables with complex spans can be hard to read on mobile.

Key Guidelines

  • Plan before coding - Sketch the table structure first. Mark spanning cells and identify which cells they replace. This takes two minutes and saves much more.
  • Add scope to headers - Use scope="col" and scope="row" on th elements in complex tables so screen readers can correctly associate header cells with their data cells.
  • Keep it as simple as possible - Deeply nested spans make tables hard to read and maintain. If the structure is getting very complex, consider whether the data could be presented differently.
  • Test on mobile - Complex tables with many spans often need a scrollable wrapper or alternative presentation on narrow screens.

Common Mistakes

The most common mistake - and the one I made repeatedly when I was learning this - is not removing cells from subsequent rows after adding a rowspan. If a cell spans 3 rows, the two rows below it each need one fewer td than usual. Browsers handle this inconsistently when the cell count is wrong: sometimes they add an extra column, sometimes cells shift in unexpected directions, and sometimes it looks fine in one browser and broken in another.

html
1<!-- Wrong: row below has one too many cells -->
2<table border="1">
3  <tr>
4    <td colspan="2">Spans two columns</td>
5  </tr>
6  <tr>
7    <td>Cell 1</td>
8    <td>Cell 2</td>
9    <td>Cell 3</td>  <!-- Extra cell - this breaks the table -->
10  </tr>
11</table>
12
13<!-- Right: row below has the correct count -->
14<table border="1">
15  <tr>
16    <td colspan="2">Spans two columns</td>
17  </tr>
18  <tr>
19    <td>Cell 1</td>
20    <td>Cell 2</td>
21  </tr>
22</table>

Common Errors

  • Wrong cell count in subsequent rows - Not removing cells from rows that are partially covered by a rowspan. Each row should only have cells for the columns not already occupied by a spanning cell.
  • Overlapping spans - Creating spans that would require two cells to occupy the same grid position. The browser will try to resolve this but results are unpredictable.
  • Missing scope attributes - In complex tables with spanning header cells, omitting scope attributes leaves screen reader users without the context to understand which header applies to which data.

Advanced Example: Class Schedule

A class schedule is a good real-world example because it naturally has both types of spanning: a lunch break that runs all day (colspan across all day columns) and lab sessions that take up two time slots (rowspan across two time rows). This is the kind of table where drawing it out first is actually necessary unless you're very comfortable with the cell-counting.

html
1<table border="1">
2  <caption>Weekly Class Schedule</caption>
3  <thead>
4    <tr>
5      <th>Time</th>
6      <th>Monday</th>
7      <th>Tuesday</th>
8      <th>Wednesday</th>
9      <th>Thursday</th>
10      <th>Friday</th>
11    </tr>
12  </thead>
13  <tbody>
14    <tr>
15      <td>9:00-10:00</td>
16      <td>Math</td>
17      <td rowspan="2">Science Lab</td>
18      <td>Math</td>
19      <td>History</td>
20      <td>Physical Education</td>
21    </tr>
22    <tr>
23      <td>10:00-11:00</td>
24      <td>English</td>
25      <!-- Tuesday covered by rowspan above -->
26      <td>English</td>
27      <td>Art</td>
28      <td>Music</td>
29    </tr>
30    <tr>
31      <td>11:00-12:00</td>
32      <td colspan="5" style="text-align: center;">Lunch Break</td>
33    </tr>
34    <tr>
35      <td>12:00-1:00</td>
36      <td>History</td>
37      <td>Math</td>
38      <td rowspan="2">Computer Lab</td>
39      <td>Science</td>
40      <td>English</td>
41    </tr>
42    <tr>
43      <td>1:00-2:00</td>
44      <td>Science</td>
45      <td>History</td>
46      <!-- Wednesday covered by rowspan above -->
47      <td>Math</td>
48      <td>Science</td>
49    </tr>
50  </tbody>
51</table>

What's Happening Here

  • Lunch break row - colspan="5" makes the lunch break span all five day columns, representing that nothing else happens during that time.
  • Lab sessions - rowspan="2" on Science Lab and Computer Lab means those cells cover two time-slot rows, and Tuesday/Wednesday respectively need one fewer cell in the row below.

Styling Tables with Colspan and Rowspan

CSS handles spanning cells the same as regular cells - they just occupy more space. One useful CSS trick is using attribute selectors to target cells that have colspan or rowspan attributes, which lets you visually distinguish merged cells from regular ones without adding extra classes.

css
1table {
2  width: 100%;
3  border-collapse: collapse;
4  margin: 1em 0;
5  font-family: Arial, sans-serif;
6}
7th, td {
8  border: 1px solid #ddd;
9  padding: 0.75em;
10  text-align: center;
11}
12th {
13  background-color: #f2f2f2;
14  font-weight: bold;
15}
16
17/* Target cells that have colspan or rowspan */
18td[colspan],
19td[rowspan] {
20  background-color: #e6f7ff;
21}
22
23/* Zebra striping */
24tbody tr:nth-child(even) {
25  background-color: #f9f9f9;
26}

Styling Techniques

  • Attribute selectors - td[colspan] and td[rowspan] target cells that have those attributes without needing extra classes on the elements.
  • Visual differentiation - A different background color on spanning cells helps users understand the table structure at a glance.

Spanning Makes Tables Match Reality

The core value of colspan and rowspan is that they let your HTML table structure match the actual structure of the data - a header that logically covers multiple columns can span those columns visually, a value that applies to multiple rows doesn't need to be repeated in every row. Once you internalize the cell-counting rule - spanning cells replace an equivalent number of cells in their affected rows and columns - the mechanics become straightforward. The class schedule example covers most of what you'll encounter in practice

Frequently Asked Questions

Can I use both colspan and rowspan on the same cell?

Yes, and it's sometimes necessary for complex layouts. A cell with colspan="2" rowspan="2" takes up a 2x2 block of the table grid - two columns wide and two rows tall. The four cells that would otherwise occupy those positions must all be omitted from the HTML. This is where drawing the layout on paper first is most useful.

Is there a limit to how many columns or rows a cell can span?

The HTML specification doesn't set a hard limit, but practical table widths mean you'll rarely need colspan beyond 10-15 and rowspan beyond 5-10. Beyond that the table becomes hard to read and maintain. If you're reaching for very large spans it might be worth asking whether the data could be restructured or presented differently.

How do screen readers handle complex tables with colspan and rowspan?

Screen readers can struggle with complex tables because they need to communicate which header applies to each data cell as users navigate cell by cell. The key accessibility attributes are scope on th elements (scope="col" for column headers, scope="row" for row headers) and id/headers pairs for very complex tables. Always test with an actual screen reader if your table has significant spanning - NVDA and VoiceOver are both free options.

Can I use percentages in colspan or rowspan?

No, they only accept positive integers representing the number of cells to span. Fractional or percentage values are invalid and will be ignored by browsers.

What happens if my colspan or rowspan values create an invalid table structure?

Browsers will attempt to render the table anyway using their own error recovery rules, but the results vary between Chrome, Firefox, Safari, and Edge - the same invalid HTML can look completely different in different browsers. The most common symptom is extra columns appearing where they shouldn't or cells shifting out of alignment. Validate your table structure and count the cells per row to make sure everything adds up.

How do I figure out the correct cell count after adding rowspan?

Each row should have cells that add up to the total column count when you include the spanning cells from above. If a 4-column table has a cell in row 1 with rowspan="2", then row 2 should only have 3 cells - the rowspan cell from row 1 is still occupying one of the 4 positions. Mentally filling in the grid position by position from top-left to bottom-right is the most reliable way to check.