How to Make a Table in HTML
To make a table in HTML, use the <table> element with one <tr> per row. Inside each row, <th> creates a header cell and <td> a normal data cell. Tables have no borders by default, so add them with CSS: border: 1px solid plus border-collapse: collapse.
Last updated: October 4, 2026
Quick answer
Step 1: Build a basic table
An HTML table is built row by row. You never create columns directly: the columns appear because every row has the same number of cells.
- Start with
<table>and</table>. - Add a row with
<tr>(table row). - In the first row, use
<th>(table header) for each column heading. Browsers show them bold and centered. - Add more rows, using
<td>(table data) for each normal cell.
Try adding a fourth row in the example below: copy one <tr> block and change the text.
Without any CSS the table has no borders and very little spacing, which makes it hard to read. Step 3 fixes that.
Step 2: Add thead, tbody and a caption
For anything beyond a tiny table, group the rows into sections and give the table a title:
<caption>: the table’s title, placed right after<table>. Screen readers announce it, which helps people understand what the table is about.<thead>: wraps the header row(s).<tbody>: wraps the main data rows.<tfoot>: wraps summary rows such as totals.
These sections don’t change how the table looks by default, but they make it easier to style (for example, only the header row), they help assistive technology, and when a long table is printed, browsers can repeat the header on every page.
Step 3: Add borders and style the table with CSS
The old border="1" attribute still works, but CSS gives you full control. Two rules matter most:
- Put
border: 1px solid #ccconthandtdto draw a line around each cell. - Put
border-collapse: collapseontable, so neighbouring cells share one line instead of showing double borders. - Add
paddingto the cells for breathing room, andtext-align: lefton headers if you prefer them aligned with the data. - Optional: color every other row with
tr:nth-child(even)(“zebra stripes”) and highlight a row on hover.
Right-aligning number columns (the num class above) makes figures much easier to compare, because the digits line up.
By default the browser sizes each column to fit its content. To control widths yourself, set width on the header cells (for example th:first-child { width: 40%; }), or add table-layout: fixed to the table: columns then take the widths you give the first row, and long text wraps instead of stretching the table. Fixed layout also renders large tables faster, because the browser doesn’t have to measure every cell first.
Step 4: Merge cells with colspan and rowspan
Sometimes one cell needs to stretch across several columns or rows, like a heading over a group of columns or a time slot that lasts two hours:
colspan="2"makes a cell two columns wide. Leave out one cell in that row to make room for it.rowspan="2"makes a cell two rows tall. Leave out one cell in the row below.
If the table suddenly looks lopsided, count the cells: every row must add up to the same number of columns once spans are included.
Step 5: Make tables work on phones
A table with many columns is wider than a phone screen. Squeezing it makes the text unreadable, so the simplest fix is to let the table scroll sideways inside its own box while the rest of the page stays put:
- Wrap the table in a
<div class="table-scroll">. - Give that div
overflow-x: auto. - Optionally give the table a
min-width, so its columns keep a readable size.
The table is wider than the preview, so drag or scroll it sideways inside its box; the rest of the page stays put.
Make tables accessible
Screen readers read tables cell by cell and announce the matching header before each value (“Age, 28”). That only works when the table tells them which cells are headers:
- Use
<th>for every header cell, including the first cell of a row when it labels that row (like the times in the schedule example above). - Add
scopewhen it isn’t obvious:scope="col"for a column header,scope="row"for a row header. - Give the table a
<caption>so people know what it shows before they start reading the cells. - Keep tables simple. Several levels of merged headers are hard to follow by ear. Splitting one complex table into two simpler ones often helps everyone.
HTML table tags at a glance
| Tag | What it does |
|---|---|
<table> | Wraps the whole table |
<caption> | The table’s title (first thing inside <table>) |
<thead>, <tbody>, <tfoot> | Group the header, body and footer rows |
<tr> | One row |
<th> | A header cell (bold, centered, announced as a heading); use scope="col" or scope="row" in complex tables |
<td> | A normal data cell |
<colgroup> / <col> | Style whole columns, e.g. a width or background |
Common mistakes when making tables
- Using tables for page layout. Tables are for data in rows and columns. Use CSS flexbox or grid to arrange a page.
- Rows with different cell counts. Every row must add up to the same number of columns (counting
colspan), or cells shift out of place. - Double borders. Forgetting
border-collapse: collapseleaves a gap between every cell’s border. - Using bold
<td>s instead of<th>. Real header cells tell screen readers what each column means. - No caption. A
<caption>(or a heading right before the table) explains what the data is. - Wide tables breaking phone layouts. Wrap them in a scrolling container.