How to make a table in HTML
A table is a <table> element made of rows (<tr>), and each row is made of cells: <th> for header cells and <td> for data cells.
The table has no borders of its own; the one CSS rule adds them so you can see the cells. The columns come from the cells: the first cell of every row is column one, the second is column two, and so on. Each row should have the same number of cells. Delete a <td> and that row ends early, leaving a gap.
<th> cells are bold and centered by default. That is only the look; what matters is that they are labels, which the next section is about.
The table elements
| Element | What it is |
|---|---|
<table> | The table |
<caption> | The table's title, the first child of <table> |
<thead> | The group of header rows |
<tbody> | The group of body rows (a table can have several) |
<tfoot> | The group of footer rows, such as totals |
<tr> | A row |
<th> | A header cell |
<td> | A data cell |
<colgroup>, <col> | Columns, for styling a whole column (widths, background) |
Borders are covered in HTML table border, and cells that span several columns or rows in colspan and rowspan.
Header cells, scope and caption
A table with headers across the top and down the side needs to say which way each header points. scope="col" labels the column below, scope="row" labels the row beside it. <caption> gives the table a visible title:
With this markup a screen reader moving to the 14:00 cell can announce "Saturday, Closes, 14:00", so the number is never read on its own. For a simple table with one header row, browsers usually work the headers out without scope, but writing it costs nothing and removes the guesswork.
The caption is also what a screen reader announces when it reaches the table. Prefer it to a heading placed above the table, which is not connected to it.
thead, tbody and tfoot
The three groups split the rows into header, body and footer. A footer is the place for totals:
Two details make the numbers readable: text-align: end lines up the decimal points (every price has two decimals), and font-variant-numeric: tabular-nums gives every digit the same width.
If you write <tr> directly inside <table>, as in the first example, the browser adds a <tbody> around the rows anyway. That is why a CSS selector like table > tr never matches anything: in the page, the rows are inside tbody. Use table tr, or write the <tbody> yourself.
Styling a table: stripes, hover and width
:nth-child stripes alternate rows, :hover highlights the row under the pointer, and width: 100% stretches the table across its container:
text-align: start aligns header text with the data below it; th is centered otherwise. start rather than left keeps the alignment correct in right-to-left languages.
Responsive tables that scroll
A table cannot shrink below the width of its content. On a phone, a wide table pushes the whole page sideways. Wrap it in a container that scrolls instead:
The container is capped at 300 pixels here to act like a phone screen; scroll it sideways. tabindex="0" lets keyboard users focus the container and scroll it with the arrow keys, and role="region" with an aria-label gives that stop a name.
Tables are for data
Use a table when the content really has rows and columns: prices, schedules, comparisons, results. Do not use one to place things side by side on the page. Layout tables confuse screen readers, which announce rows and columns that mean nothing, and they cannot adapt to small screens. CSS flexbox and grid do layout.
Common mistakes
- Rows with different numbers of cells. The short rows end early and the columns stop lining up.
- Using
<td>with bold text for headers. Use<th>so the headers are real labels. - The obsolete attributes
border,cellpadding,cellspacing,bgcolorandalign. Use CSS instead. - Putting content directly in
<tr>or<table>, outside any cell. Browsers move it out of the table, usually above it. - A heading above the table instead of a
<caption>. table > trin CSS. The browser has inserted a<tbody>; writetable trortbody tr.
Frequently Asked Questions
How do you create a table in HTML?
Wrap it in <table>, write one <tr> per row, and put the cells inside: <th> for header cells and <td> for data cells. Every row should have the same number of cells, unless some cells span several columns.
What is the difference between th and td?
<th> is a header cell that labels a column or row, and <td> is a data cell. Browsers show <th> bold and centered, and screen readers read the matching header before each data cell so the numbers make sense.
What do thead, tbody and tfoot do?
They group rows into the header, the body and the footer (totals) of the table. They make the structure clear to assistive technology, give CSS something to target, and when a long table is printed, browsers can repeat the <thead> on every page.
Why does my HTML table have no borders?
Tables have no borders by default. Add them with CSS: table { border-collapse: collapse; } th, td { border: 1px solid #ccc; }. The old border="1" attribute is obsolete.
How do I make an HTML table responsive?
Put the table in a container with overflow-x: auto. On a narrow screen the table scrolls sideways inside the container instead of making the whole page wider.