Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How do I mark up a data table with clear row and column headers?

Quick answer

For a genuine data table, mark header cells with <th> and ordinary data cells with <td>. In a straightforward table, use scope="col" for headers that label columns and scope="row" for headers that label rows. More complex header relationships may need id and headers instead. Use a table for related tabular data, not just to position page content. [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I have a small table of products and prices. What makes it a data table, and how should I mark it up?

Geez

It is a data table when the cells show relationships between items and their details—for example, each product has a corresponding price. For that kind of table, use <th> for cells that identify headers and <td> for the values. In a straightforward table, scope="col" identifies a header for a column, while scope="row" identifies a header for a row. [S1]

Here is a copyable example. The product names label rows; the two column headers label the values in each column. The names and prices are illustrative placeholders, not product recommendations.

<table>
  <tr>
    <th scope="col">Product</th>
    <th scope="col">Unit price</th>
    <th scope="col">In stock</th>
  </tr>
  <tr>
    <th scope="row">[Product A]</th>
    <td>[Price]</td>
    <td>[Quantity]</td>
  </tr>
  <tr>
    <th scope="row">[Product B]</th>
    <td>[Price]</td>
    <td>[Quantity]</td>
  </tr>
</table>

The header cells are not just visually prominent text: they are marked as headers, while the entries are marked as data cells. [S1]

Follow-up question

Why make a product name a row header instead of leaving it as a regular data cell?

Geez

In this example, the product name identifies the other values across its row. Marking it as <th scope="row"> expresses that row-header relationship; <td> marks the price and stock entries as data. Likewise, the top labels identify the columns, so they use <th scope="col">. This is a practical way to make the relationships in a straightforward table explicit in the markup. [S1]

As a review question, ask: “Which label tells me what this value means?” If a label applies to every entry beneath it, it belongs in a column-header position. If it identifies the other values alongside it, it may be a row header. Check that each value has a clear label in the table structure, not merely a bold or centered appearance.

Follow-up question

I have also used a table to line up a logo beside a paragraph. Should I mark those cells as headers?

Geez

First ask whether the content is genuinely tabular: do the cells express relationships between a set of headers and their data? If you are using a table only to arrange a logo beside a paragraph, that is page layout rather than the data-table use described by the W3C tutorial. The tutorial addresses data tables and distinguishes them from tables used merely for layout. [S1]

So do not add row or column headers just to make layout content look structured. Choose markup suited to the content’s actual purpose. If the content is a data table, identify its header cells with <th> and its data cells with <td>; if it is only a visual arrangement, reconsider whether a table is appropriate.

Follow-up question

What if my table has grouped headings, such as quarterly sales split by region? Is the simple example enough?

Geez

Not necessarily. The simple example fits a straightforward structure where column or row direction makes the header relationship clear. For more complex relationships, the W3C tutorial describes using id and headers to associate data cells with their relevant headers. [S1] Avoid assuming that adding scope alone explains every grouped or multi-level heading arrangement.

Before implementing a complex table, sketch which headers apply to each data cell. If the relationships are difficult to describe clearly, simplify the table or get help reviewing the markup. The cited guidance establishes that id and headers may be needed for complex relationships; it does not prescribe a particular structure for every grouped-heading design. [S1]

A practical checklist

  • Confirm the content is related tabular data, not a table used only for page layout.

  • Mark header cells with <th> and ordinary data cells with <td>.

  • Use scope="col" for straightforward column headers and scope="row" for straightforward row headers.

  • Check that each row and column label clearly describes the values it relates to.

  • For grouped or otherwise complex headers, map the relationships and consider id and headers.

A few more useful answers

Can I make a data cell look like a header without using `<th>`?

Visual styling alone does not mark a cell as a header. For a data table, use <th> for header cells and <td> for data cells. [S1]

Do all tables need both row and column headers?

No. Use headers that match the actual relationships in the table. A straightforward table may have column headers, row headers, or both; the example uses both to make its labels clear.

What does this guidance establish about complex tables?

It establishes that complex header relationships may require id and headers, but it does not give a complete design for every multi-level table. [S1]

Sources

References used for this guide.

  1. [S1] W3C WAI Tables Tutorial: Marking up data-table headers
    w3.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work