Ad – 728Γ—90
🌐 Core Elements

HTML Tables – Structured Tabular Data

HTML tables are designed for one purpose: displaying tabular data β€” information that has a natural row-and-column structure, like a spreadsheet, timetable, or comparison chart. Modern HTML provides a rich set of elements (thead, tbody, tfoot, caption, th with scope) that make tables both well-structured and accessible.

⏱️ 14 min read🎯 BeginnerπŸ“… Updated 2026

Basic Table: table, tr, td

The three core table elements are:

  • <table> β€” the wrapper element for the entire table
  • <tr> β€” a table row
  • <td> β€” a table data cell (standard cell)
HTML
<table>
  <tr>
    <td>HTML</td>
    <td>Structure</td>
    <td>Beginner</td>
  </tr>
  <tr>
    <td>CSS</td>
    <td>Styling</td>
    <td>Beginner</td>
  </tr>
  <tr>
    <td>JavaScript</td>
    <td>Interactivity</td>
    <td>Intermediate</td>
  </tr>
</table>
Result: A 3Γ—3 grid of cells. By default the browser renders tables with no borders β€” you need CSS to add visible borders.

Table Headers – th and scope

The <th> element defines a header cell. Unlike <td>, a <th> is bold and centred by default, and it carries semantic meaning β€” it labels a column or row.

The scope attribute tells screen readers whether the header applies to a column (scope="col") or a row (scope="row").

HTML
<table>
  <tr>
    <th scope="col">Language</th>
    <th scope="col">Purpose</th>
    <th scope="col">Level</th>
  </tr>
  <tr>
    <td>HTML</td>
    <td>Structure</td>
    <td>Beginner</td>
  </tr>
  <tr>
    <td>CSS</td>
    <td>Styling</td>
    <td>Beginner</td>
  </tr>
</table>

Table Structure: thead, tbody, tfoot

For any table beyond the simplest, wrap your rows in the semantic section elements:

  • <thead> β€” the header section (column labels)
  • <tbody> β€” the body (data rows)
  • <tfoot> β€” the footer (totals, notes)

These elements don't change visual rendering by default, but they allow browsers to keep the header and footer visible when the table body scrolls, and they help screen readers and CSS target sections independently.

HTML
<table>
  <thead>
    <tr>
      <th scope="col">Course</th>
      <th scope="col">Lessons</th>
      <th scope="col">Duration</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>HTML Fundamentals</td>
      <td>24</td>
      <td>3 hours</td>
    </tr>
    <tr>
      <td>CSS Basics</td>
      <td>30</td>
      <td>4 hours</td>
    </tr>
    <tr>
      <td>JavaScript Essentials</td>
      <td>45</td>
      <td>6 hours</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td><strong>Total</strong></td>
      <td><strong>99</strong></td>
      <td><strong>13 hours</strong></td>
    </tr>
  </tfoot>
</table>

The caption Element

The <caption> element provides a visible title for a table. It must be the first child of <table>. Both sighted users and screen readers benefit from a clear caption β€” it explains what the table is about without relying on surrounding text.

HTML
<table>
  <caption>ylearner Course Catalogue – 2026</caption>
  <thead>
    <tr>
      <th scope="col">Course</th>
      <th scope="col">Level</th>
      <th scope="col">Free?</th>
    </tr>
  </thead>
  <tbody>
    <tr><td>HTML</td><td>Beginner</td><td>Yes</td></tr>
    <tr><td>Python</td><td>Beginner</td><td>Yes</td></tr>
    <tr><td>JavaScript</td><td>Intermediate</td><td>Yes</td></tr>
  </tbody>
</table>
Ad – 336Γ—280

colspan and rowspan – Merging Cells

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

HTML
<!-- colspan: cell spans 2 columns -->
<table>
  <caption>Weekly Schedule</caption>
  <thead>
    <tr>
      <th scope="col">Time</th>
      <th scope="col">Monday</th>
      <th scope="col">Tuesday</th>
      <th scope="col">Wednesday</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">9:00 AM</th>
      <td colspan="2">HTML Workshop (spans Mon + Tue)</td>
      <td>CSS Intro</td>
    </tr>
    <tr>
      <th scope="row">11:00 AM</th>
      <td>JS Basics</td>
      <td rowspan="2">Project Work (spans 11 AM + 1 PM)</td>
      <td>Python Intro</td>
    </tr>
    <tr>
      <th scope="row">1:00 PM</th>
      <td>Review</td>
      <td>Git Basics</td>
    </tr>
  </tbody>
</table>
When using colspan / rowspan: count your cells carefully. If a row should have 3 cells but one cell has colspan="2", that row only needs 2 <td> elements total. A cell count mismatch causes columns to misalign.

Use Tables for Data Only

Tables are NOT for layout. In the 1990s developers used tables to position page columns. This practice is obsolete β€” use CSS Flexbox or Grid for layout. Table misuse causes serious accessibility problems because screen readers announce cell relationships that don't exist. Only use <table> when the content is genuinely tabular (it has meaningful rows AND meaningful columns).

Good use cases for tables:

  • Comparison charts (features vs. pricing tiers)
  • Timetables and schedules
  • Sports results / league tables
  • Financial data and reports
  • Specification sheets

Accessibility Best Practices

An accessible table communicates the relationship between each data cell and its header. Key practices:

  • Always use <th> for header cells β€” never style a <td> to look like a header.
  • Add scope="col" on column headers and scope="row" on row headers.
  • Use <caption> to give the table a descriptive title.
  • For complex tables with multiple header rows, use the headers attribute on data cells to point to the relevant header cell IDs.
HTML
<!-- Fully accessible table with id/headers association -->
<table>
  <caption>Course pricing by plan</caption>
  <thead>
    <tr>
      <th id="course" scope="col">Course</th>
      <th id="free" scope="col">Free Plan</th>
      <th id="pro" scope="col">Pro Plan</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th id="html-row" scope="row">HTML</th>
      <td headers="free html-row">All lessons</td>
      <td headers="pro html-row">All lessons + projects</td>
    </tr>
    <tr>
      <th id="py-row" scope="row">Python</th>
      <td headers="free py-row">First 10 lessons</td>
      <td headers="pro py-row">Full course + mentoring</td>
    </tr>
  </tbody>
</table>

πŸ“‹ Summary

  • Core elements: <table>, <tr>, <td> (data cell), <th> (header cell).
  • Semantic structure: <thead>, <tbody>, <tfoot> divide the table into logical sections.
  • <caption> gives the table a visible, accessible title β€” always include one.
  • colspan merges cells horizontally; rowspan merges cells vertically.
  • Use scope="col" / scope="row" on <th> elements for screen reader accessibility.
  • Tables are for tabular data only β€” never use them for page layout.

Frequently Asked Questions

Do I need thead and tbody on every table?

Technically no β€” they are optional. But using them is considered best practice. They improve code readability, enable independent CSS styling of sections, and help browsers handle long tables in print or overflow scenarios (some browsers keep <thead> visible at the top of each printed page).

How do I add borders to an HTML table?

Use CSS. The old HTML border attribute is deprecated. A modern approach:

CSS
table {
  border-collapse: collapse; /* Remove double borders */
  width: 100%;
}

th, td {
  border: 1px solid #dee2e6;
  padding: 0.5rem 0.75rem;
  text-align: left;
}

thead tr {
  background-color: #f8f9fa;
}

Can I make a table responsive?

Yes. Wrap the <table> in a <div> with overflow-x: auto β€” this lets the table scroll horizontally on small screens without breaking the page layout. For complex tables, consider a mobile-specific CSS approach that reformats rows as stacked cards on narrow screens.

What is the difference between td and th?

<td> is a standard data cell β€” it holds the actual data values. <th> is a header cell β€” it labels a row or column. Browsers render <th> bold and centred by default, but more importantly, screen readers announce it as a header, giving users context for the data cells it governs.