HTML Table Basics: Building Semantic Tables with <table>, <tr>, <th>, and <td>

HTML tables are the standard way to present data that belongs in rows and columns, such as schedules, price lists, reports, and comparison charts. This article shows you how to build tables correctly, add headers and captions, and keep your markup accessible and semantic.

Quick answer: Use <table> for tabular data, <tr> for rows, <th> for header cells, and <td> for regular data cells. Add <caption> for a short table description and use scope or structural headings to make the table easier to understand with screen readers.

Difficulty: Beginner

You'll understand this better if you know: basic HTML elements, how attributes work, and the difference between content meant for layout versus content meant for data.

1. What Are HTML Table Basics?

HTML table basics cover the core elements and rules for writing tables in HTML. A proper table is not just a grid on the page; it is structured data with relationships between headers and cells.

These elements work together to help browsers, assistive technologies, and developers understand how the table is organized.

2. Why HTML Table Basics Matter

Tables are important because they communicate relationships that would be hard to express with paragraphs or lists. When used correctly, they make data easier to scan, compare, and reference.

Good table structure also improves accessibility. Screen readers can announce headers and navigate cells more effectively when the markup is semantic and consistent.

Tables should be used for data, not for page layout. Using tables to position page sections makes the HTML harder to maintain and often creates accessibility problems.

3. Core Table Structure and Syntax

A simple HTML table follows a predictable pattern. The table element contains rows, and each row contains header or data cells.

Basic table skeleton

This example shows the minimum structure of a semantic table with a caption and a header row.

<table>
  <caption>Monthly expenses</caption>
  <tr>
    <th scope="col">Category</th>
    <th scope="col">Amount</th>
  </tr>
  <tr>
    <td>Rent</td>
    <td>$1,200</td>
  </tr>
</table>

The caption describes the table, the first row uses header cells, and the data rows use regular cells. That structure is enough for many simple tables.

What each element does

<table> is the container. <tr> marks a row. <th> labels a column or row. <td> contains the actual values.

The scope attribute helps clarify whether a header applies to a column or a row. This is especially useful when the table has more than one header direction.

4. Step-by-Step Examples

The following examples show how table basics work in real situations. Each one uses the same core elements, but the structure changes based on the data.

Example 1: A simple product list

This table lists products and prices. It is the most common beginner-friendly pattern: one header row and several data rows.

<table>
  <caption>Featured products</caption>
  <tr>
    <th scope="col">Product</th>
    <th scope="col">Price</th>
  </tr>
  <tr>
    <td>Notebook</td>
    <td>$4.99</td>
  </tr>
  <tr>
    <td>Pen</td>
    <td>$1.49</td>
  </tr>
</table>

This version is easy to read and easy for assistive technology to interpret. Each column has a clear label.

Example 2: A schedule table with row headers

Sometimes the first column is also a header column. In that case, use <th> in the leftmost cells too.

<table>
  <caption>Class schedule</caption>
  <tr>
    <th scope="col">Day</th>
    <th scope="col">Topic</th>
    <th scope="col">Time</th>
  </tr>
  <tr>
    <th scope="row">Monday</th>
    <td>HTML basics</td>
    <td>9:00 AM</td>
  </tr>
</table>

The first cell in the data row is a header because it labels the row. This pattern helps readers understand which row they are looking at.

Example 3: A table with grouped information

When rows belong to different categories, you can group them visually and structurally. This table keeps the data readable without adding unnecessary complexity.

<table>
  <caption>Support hours</caption>
  <tr>
    <th scope="col">Team</th>
    <th scope="col">Available</th>
  </tr>
  <tr>
    <td>Billing</td>
    <td>Mon-Fri, 8 AM-5 PM</td>
  </tr>
  <tr>
    <td>Technical</td>
    <td>24/7</td>
  </tr>
</table>

This layout is useful when the table has a small number of columns and the rows are the main thing the user compares.

Example 4: A compact comparison table

Tables are often used to compare features, plans, or options. A clear header row and consistent cell content make the comparison easier to scan.

<table>
  <caption>Plan comparison</caption>
  <tr>
    <th scope="col">Feature</th>
    <th scope="col">Basic</th>
    <th scope="col">Pro</th>
  </tr>
  <tr>
    <th scope="row">Email support</th>
    <td>No</td>
    <td>Yes</td>
  </tr>
</table>

The row headers make it obvious what each row is about, which reduces confusion in wider tables.

5. Practical Use Cases

HTML table basics are useful whenever the content is organized as data rather than as flowing text.

If you can describe the content as rows and columns that someone needs to compare, a table is probably the right element.

6. Common Mistakes

Mistake 1: Using tables for page layout

New developers sometimes use tables to position headers, sidebars, and page sections. That may seem convenient at first, but it creates rigid markup that is harder to maintain and less accessible.

Problem: This table is being used to arrange page layout instead of tabular data, which makes the HTML harder to read and can confuse assistive technology.

<table>
  <tr>
    <td>Site logo</td>
    <td>Navigation</td>
  </tr>
  <tr>
    <td>Main content</td>
    <td>Sidebar</td>
  </tr>
</table>

Fix: Use semantic layout elements like <header>, <nav>, <main>, <aside>, and <footer> instead.

<header>Site logo</header>
<nav>Navigation</nav>
<main>
  <article>Main content</article>
</main>
<aside>Sidebar</aside>

The corrected version works because it uses elements that match the meaning of the content.

Mistake 2: Putting data cells where header cells belong

If you use <td> for headings, the table still appears on the page, but the relationships between rows and columns become less clear.

Problem: This table does not identify the headers, so screen readers and other tools have less information about how the cells relate.

<table>
  <tr>
    <td>Name</td>
    <td>Email</td>
  </tr>
  <tr>
    <td>Ava</td>
    <td>[email protected]</td>
  </tr>
</table>

Fix: Use <th> for header cells, and add scope when it improves clarity.

<table>
  <tr>
    <th scope="col">Name</th>
    <th scope="col">Email</th>
  </tr>
  <tr>
    <td>Ava</td>
    <td>[email protected]</td>
  </tr>
</table>

The corrected version clearly marks the headers, which makes the table easier to interpret.

Mistake 3: Omitting a caption or table context

When a table appears in a long page, readers may not immediately know what it is about. A missing caption is not a browser error, but it is a common usability problem.

Problem: The table has no caption or descriptive text, so the purpose of the data may not be obvious to sighted users or screen reader users.

<table>
  <tr>
    <th scope="col">Month</th>
    <th scope="col">Revenue</th>
  </tr>
  <tr>
    <td>January</td>
    <td>$12,400</td>
  </tr>
</table>

Fix: Add a short caption that explains what the data represents.

<table>
  <caption>Monthly revenue report</caption>
  <tr>
    <th scope="col">Month</th>
    <th scope="col">Revenue</th>
  </tr>
  <tr>
    <td>January</td>
    <td>$12,400</td>
  </tr>
</table>

The corrected version works better because the caption gives the table immediate context.

7. Best Practices

Practice 1: Use tables only for true tabular data

Reserve tables for content that naturally belongs in rows and columns. This keeps your HTML honest and makes the document easier to maintain.

<table>
  <caption>Office locations</caption>
  <tr>
    <th scope="col">City</th>
    <th scope="col">Phone</th>
  </tr>
</table>

This matters because semantic accuracy improves accessibility and makes future edits less confusing.

Practice 2: Include a caption for context

A caption helps users understand the table before they inspect individual cells. It is especially useful when several tables appear on the same page.

<table>
  <caption>Q3 attendance summary</caption>
  <tr>
    <th scope="col">Team</th>
    <th scope="col">Present</th>
  </tr>
</table>

This helps both sighted users and screen reader users identify the purpose of the data quickly.

Practice 3: Keep header relationships obvious

When a table has more than one level of meaning, use row and column headers consistently. This prevents ambiguity as the table grows.

<table>
  <caption>Weekly activity</caption>
  <tr>
    <th scope="col">Day</th>
    <th scope="col">Steps</th>
  </tr>
  <tr>
    <th scope="row">Monday</th>
    <td>8,200</td>
  </tr>
</table>

This approach makes the table easier to scan and less error-prone when new rows are added.

8. Limitations and Edge Cases

Note: Some browsers render table spacing and alignment slightly differently when content is long or when cells contain form controls, so test your table in the environments where it will be used.

9. Practical Mini Project

Let’s build a small, complete pricing table that uses a caption, column headers, row headers, and consistent data cells. This is a realistic pattern for landing pages and product comparisons.

<table>
  <caption>Subscription pricing</caption>
  <tr>
    <th scope="col">Plan</th>
    <th scope="col">Monthly cost</th>
    <th scope="col">Support</th>
  </tr>
  <tr>
    <th scope="row">Starter</th>
    <td>$9</td>
    <td>Email only</td>
  </tr>
  <tr>
    <th scope="row">Business</th>
    <td>$29</td>
    <td>Priority email</td>
  </tr>
</table>

This table is complete enough to be useful in a real page, but still simple enough to understand at a glance. It also shows the most important table basics in one place.

10. Key Points

11. Practice Exercise

Create a table for a fictional bookstore inventory with these requirements:

Expected output: A semantic table that clearly lists book inventory data and can be understood without extra explanation.

Hint: Start with the caption, then write the header row, then add one row per book.

Solution:

<table>
  <caption>Bookstore inventory</caption>
  <tr>
    <th scope="col">Book</th>
    <th scope="col">Author</th>
    <th scope="col">Stock</th>
  </tr>
  <tr>
    <td>HTML and CSS Basics</td>
    <td>A. Rivera</td>
    <td>12</td>
  </tr>
  <tr>
    <td>Accessible Web Design</td>
    <td>M. Chen</td>
    <td>8</td>
  </tr>
  <tr>
    <td>Modern HTML Reference</td>
    <td>J. Patel</td>
    <td>5</td>
  </tr>
</table>

12. Final Summary

HTML table basics are about structuring data clearly, not just drawing a grid on the page. A well-built table uses <table>, <tr>, <th>, and <td> in a way that matches the meaning of the content.

For most tables, the best starting point is simple: add a caption, use header cells for labels, and keep each row consistent. That approach makes the table easier to read, easier to maintain, and much more accessible.

If you want to go further, the next step is learning how rowspan, colspan, and more advanced table accessibility patterns work in larger tables.