HTML Tables Tutorial: Complete Guide to Structure, Styling, Accessibility & Responsive Design (2026)

Realistic 3D editorial illustration of an HTML table with rows, columns, header cells, merged cells, and HTML code structure for a complete HTML tables tutorial.

An HTML table is built with the <table> element, using <tr> for rows and <td> for data cells, with <th> marking header cells, and it’s still the correct, most accessible way to display genuinely tabular data like price lists, schedules, and comparison charts. What’s changed since older table tutorials were written isn’t the core structure, it’s everything around it: several styling attributes commonly taught a few years ago, including bgcolor, background, and bordercolor, are now deprecated in HTML5 and should be replaced with CSS, and modern tables need accessibility markup and responsive handling that older guides skip entirely. Here’s the complete, current version, structure through styling through accessibility.

What Is an HTML Table, and When Should You Use One?

A table arranges data into rows and columns of cells, and it exists specifically for genuinely tabular data, information where the row-and-column relationship itself is meaningful, like a spreadsheet. It is not meant for page layout. That distinction matters more than it might seem: using tables to lay out a whole webpage was common practice in the early 2000s but is now considered an outdated anti-pattern, since it creates real accessibility problems and makes responsive design far harder. If you need to arrange page sections, use CSS Grid or Flexbox instead; reserve <table> for actual tabular data.

Basic HTML Table Structure

Every table starts with the same three building blocks:

<table>
  <tr>
    <th>Name</th>
    <th>Favorite Color</th>
  </tr>
  <tr>
    <td>Bob</td>
    <td>Yellow</td>
  </tr>
  <tr>
    <td>Michelle</td>
    <td>Purple</td>
  </tr>
</table>
  • <table> defines the table itself.
  • <tr> (table row) creates each horizontal row.
  • <td> (table data) creates a standard data cell, left-aligned by default.
  • <th> (table header) creates a header cell, centered and bold by default, and should be used for any cell functioning as a column or row header rather than as a styling shortcut.

Table markup is always built row by row. There’s no dedicated “column” tag; columns exist as the visual result of consistent cell position across rows.

Table Headers Done Right (And Why Scope Matters)

Using <th> for header cells is only half the job. For genuine accessibility, and something older table tutorials consistently skip, each <th> should include a scope attribute so assistive technology can programmatically determine which header applies to which data cell:

<table>
  <tr>
    <th scope="col">Name</th>
    <th scope="col">Favorite Color</th>
  </tr>
  <tr>
    <th scope="row">Bob</th>
    <td>Yellow</td>
  </tr>
</table>

Use scope="col" for column headers and scope="row" for row headers. For simple tables, this is sufficient. For complex tables with headers spanning multiple levels or applying irregularly, the more explicit id and headers attributes should be used instead, so the header-to-cell association is unambiguous rather than inferred.

Grouping Table Content: thead, tbody, and tfoot

Larger tables benefit from being split into semantic sections:

<table>
  <thead>
    <tr>
      <th scope="col">Product</th>
      <th scope="col">Price</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Widget</td>
      <td>$9.99</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td>Total</td>
      <td>$9.99</td>
    </tr>
  </tfoot>
</table>

<thead> holds the header row (or rows), <tbody> holds the main data, and <tfoot> holds summary or footer content, such as a total row. A table can contain multiple <tbody> elements to represent distinct groups of data, but <thead> and <tfoot> must both appear in the markup before any <tbody> element, even though <tfoot> visually renders at the bottom.

The Table Caption: Still Valid, Not Deprecated

Here’s a correction worth making clearly: the <caption> element itself is not deprecated. Some older tutorials get this wrong, likely confusing it with the caption’s own align attribute, which is deprecated. The <caption> tag remains the current, recommended way to give a table an accessible name and description, and it must be the first child inside the <table> element:

<table>
  <caption>Monthly Sales by Region</caption>
  <thead>
    <tr>
      <th scope="col">Region</th>
      <th scope="col">Sales</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>North</td>
      <td>$12,400</td>
    </tr>
  </tbody>
</table>

Beyond accessibility, a caption is crawlable, indexable text that helps both search engines and AI-powered search features understand what your table actually contains, something a purely visual title above the table doesn’t provide the same way.

Merging Cells: colspan and rowspan

Use colspan to make a cell span multiple columns, and rowspan to make a cell span multiple rows:

<table>
  <tr>
    <th colspan="2">Contact Information</th>
  </tr>
  <tr>
    <td>Phone</td>
    <td>555-0100</td>
  </tr>
</table>

Use both attributes sparingly. Overlapping spans can complicate how screen readers interpret the table structure, and a table that needs extensive merged cells to display correctly is sometimes a sign the data would be clearer split into two simpler tables instead.

Styling Tables the Modern Way (Not With bgcolor)

This is where the biggest, most important update from older HTML table tutorials comes in. Attributes like bgcolor, background, border, bordercolor, cellpadding, cellspacing, and align are all either formally deprecated or non-standard in HTML5, according to MDN’s own table element reference. They may still render in most browsers for backward compatibility, but they shouldn’t be used in new code. The replacement, across the board, is CSS:

table {
  border-collapse: collapse;
  width: 100%;
}

th, td {
  border: 1px solid #ccc;
  padding: 10px;
  text-align: left;
}

th {
  background-color: #f4f4f4;
}

tr:nth-child(even) {
  background-color: #fafafa;
}

tr:hover {
  background-color: #f1f1f1;
}

A quick mapping from the old attributes to their CSS replacements:

Deprecated HTML attributeModern CSS replacement
bgcolorbackground-color
backgroundbackground-image
border / bordercolorborder / border-color
cellpaddingpadding on th/td
cellspacingborder-spacing on table
aligntext-align
widthwidth (as CSS, not an HTML attribute)

border-collapse: collapse is worth calling out specifically: by default, HTML tables render with visible gaps between adjacent cell borders (double borders), and border-collapse: collapse merges them into clean single lines, which is almost always the visual result people actually want.

Making Tables Accessible

Beyond the scope attribute and <caption> covered above, the W3C Web Accessibility Initiative’s own tables tutorial lays out the core principle clearly: a table without structural markup connecting header and data cells creates a real barrier for screen reader users, since visual layout alone doesn’t communicate those relationships to assistive technology. A few additional practices worth following:

  • Always use <th>, never a styled <td>, for anything functioning as a header, even if it’s visually indistinguishable from a data cell.
  • Keep tables meant for genuine data separate from any layout use, since a screen reader will announce a data table’s structure (row X of Y, column header Z) in a way that’s actively confusing if the table is really just holding page layout.
  • For complex tables with multi-level headers, use explicit id and headers attribute pairs rather than relying on scope alone.

Making Tables Responsive

A table sized for a desktop screen will often overflow on mobile, and this is an area older table tutorials from 2020 and earlier simply didn’t need to address as urgently. Two common patterns handle this:

Horizontal scroll container, the simplest fix for most tables:

.table-wrapper {
  overflow-x: auto;
  max-width: 100%;
}
<div class="table-wrapper">
  <table>
    </table>
</div>

This wraps the table in a container that scrolls horizontally on narrow screens rather than breaking the page layout, preserving the table’s structure exactly as-is.

Stacked rows pattern, better for tables with a moderate number of columns, where each row reflows into a vertical block on small screens via CSS media queries, keeping each row’s data grouped together for readability rather than requiring horizontal scrolling at all. This requires more CSS setup than the scroll-container approach but often reads more naturally on a phone screen.

Frequently Asked Questions

Is the bgcolor attribute still usable in HTML tables? It still renders in most browsers for backward compatibility, but it’s deprecated in HTML5 and shouldn’t be used in new code. Use the CSS background-color property instead.

Is the caption tag deprecated in HTML5? No, the <caption> element itself is not deprecated and remains the current, recommended way to give a table an accessible name. Only the caption’s own align attribute is deprecated; the element is fine to use.

What’s the difference between colspan and rowspan? colspan makes a table cell span multiple columns horizontally. rowspan makes a cell span multiple rows vertically. Both accept an integer value for how many columns or rows to span.

Do thead and tfoot need to appear in a specific order in the code? Yes. Both <thead> and <tfoot> must appear in the HTML before any <tbody> element, even though <tfoot> visually displays at the bottom of the rendered table.

Should I use HTML tables for page layout? No. Using tables for layout is considered an outdated, inaccessible practice. Use CSS Grid or Flexbox for page layout, and reserve <table> specifically for genuinely tabular data.

How do I make an HTML table responsive on mobile? The simplest method wraps the table in a container styled with overflow-x: auto, allowing horizontal scrolling on small screens without altering the table’s structure. For tables with fewer columns, a stacked-rows CSS pattern that reflows each row into a vertical block on mobile is often more readable.

Why does my table have double borders between cells? By default, adjacent table cell borders don’t merge, creating a doubled-border appearance. Add border-collapse: collapse to your table’s CSS to merge them into single, clean borders.

Conclusion

The core structure of an HTML table, <table>, <tr>, <td>, and <th>, hasn’t changed in years and doesn’t need to. What has changed is everything that used to be handled with HTML attributes directly: styling now belongs in CSS, not bgcolor or border attributes, and modern tables need scope attributes, a proper <caption>, and a responsive strategy to hold up to current accessibility and mobile-usability standards. If you’re maintaining an older table built with deprecated attributes, migrating the styling into CSS and adding the accessibility markup above is worth doing sooner than later, since those attributes could see further browser deprecation over time even though most still render for now.

Related: For the fundamentals of HTML structure before diving into tables specifically, see our HTML tutorial for beginners.

I have been serving web content with my passionate writing skills since 2020. My skills have benefited clients from 20 countries, resulting in 10x audience interactions, improved readability, and SEO-friendly content.
Exit mobile version