<table> / <tr> / <td> / <th>
| Since: | HTML 4(1997) |
|---|
The <table> element creates a table in HTML. Use <tr> to define rows, <td> for data cells, and <th> for header cells. You can also group rows with <thead>, <tbody>, and <tfoot>, and add a title with <caption>.
Syntax
<table>
<caption>Table Title</caption>
<thead>
<tr>
<th>Header 1</th>
<th>Header 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data 1</td>
<td>Data 2</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer 1</td>
<td>Footer 2</td>
</tr>
</tfoot>
</table>
Tags and Attributes
| Tag / Attribute | Description |
|---|---|
| <table> | The container element that wraps the entire table. |
| <tr> | Represents a table row (Table Row). |
| <td> | Represents a table data cell (Table Data). Used for regular content. |
| <th> | Represents a table header cell (Table Header). Text is bold and centered by default. |
| <caption> | Specifies a title or heading for the table. Must be the first child element of <table>. |
| <thead> | Represents the header row group of a table. Contains rows with column headings. |
| <tbody> | Represents the body (data rows) group of a table. Both the opening and closing tags can be omitted — the browser will add them automatically. |
| <tfoot> | Represents the footer row group of a table. Used for totals, summaries, or notes. |
| <colgroup> | Groups columns together. Used to apply CSS styles to entire columns at once. |
| <col> | An empty element inside <colgroup> that represents individual columns. Use the span attribute to cover multiple columns. |
| scope attribute | Specifies which cells a <th> header relates to. Accepted values are col (column), row (row), colgroup, and rowgroup. |
| colspan attribute | Merges a cell horizontally across multiple columns. The value specifies the number of columns to span. Used on <td> and <th>. |
| rowspan attribute | Merges a cell vertically across multiple rows. The value specifies the number of rows to span. Used on <td> and <th>. |
| headers attribute | Specifies the id values of <th> elements that a <td> relates to (space-separated). An accessibility attribute for clarifying cell-header relationships in complex tables. |
| abbr attribute | Specifies an abbreviated form of a <th> element's content. Screen readers may read this value instead of the full text. |
| span attribute | Specifies the number of columns that a <colgroup> or <col> element covers. |
Sample Code
<!-- Table with column headers (scope="col") -->
<table border="1">
<tr>
<!-- scope="col" marks these as column headers -->
<th scope="col">Name</th>
<th scope="col">Language</th>
<th scope="col">Purpose</th>
</tr>
<tr>
<td>HTML</td>
<td>Markup language</td>
<td>Web page structure</td>
</tr>
<tr>
<td>CSS</td>
<td>Stylesheet language</td>
<td>Web page appearance</td>
</tr>
<tr>
<td>JavaScript</td>
<td>Programming language</td>
<td>Web page behavior</td>
</tr>
</table>
<!-- Table with row headers (scope="row") -->
<table border="1">
<tr>
<th scope="col">Subject</th>
<th scope="col">Term 1</th>
<th scope="col">Term 2</th>
<th scope="col">Term 3</th>
</tr>
<tr>
<!-- scope="row" marks this as a row header -->
<th scope="row">Math</th>
<td>85</td>
<td>90</td>
<td>88</td>
</tr>
<tr>
<th scope="row">English</th>
<td>72</td>
<td>78</td>
<td>81</td>
</tr>
</table>
Result
A table with 3 columns and 4 rows is displayed. The first row contains bold headers (Name, Language, Purpose), and rows 2–4 contain the data for each language.
scope="col" explicitly indicates that a header cell applies to cells in the same column. It does not change the appearance, but it helps assistive technologies such as screen readers correctly associate headers with their data cells.
The second table uses scope="row", which means "this header applies to cells in the same row." Use it on <th> elements at the beginning of a row. As a rule of thumb, use scope="col" for column headers and scope="row" for row headers.
Table Grouping (thead / tbody / tfoot / caption)
Use <thead>, <tbody>, and <tfoot> to group table rows into header, body, and footer sections. The <caption> element adds a title to the table and must be the first child of <table>.
<table border="1">
<!-- Table title -->
<caption>Programming Language Study Hours</caption>
<!-- Header row group -->
<thead>
<tr>
<th scope="col">Language</th>
<th scope="col">Difficulty</th>
<th scope="col">Study Hours</th>
</tr>
</thead>
<!-- Data row group -->
<tbody>
<tr>
<td>HTML</td>
<td>Beginner</td>
<td>10</td>
</tr>
<tr>
<td>CSS</td>
<td>Beginner</td>
<td>20</td>
</tr>
<tr>
<td>JavaScript</td>
<td>Intermediate</td>
<td>40</td>
</tr>
<tr>
<td>PHP</td>
<td>Intermediate</td>
<td>30</td>
</tr>
<tr>
<td>Python</td>
<td>Intermediate</td>
<td>25</td>
</tr>
</tbody>
<!-- Footer row group (totals, etc.) -->
<tfoot>
<tr>
<td colspan="2">Total</td>
<td>125</td>
</tr>
</tfoot>
</table>
The header row (Language, Difficulty, Study Hours) is wrapped in <thead>, the five data rows are in <tbody>, and the total row is in <tfoot>. The <caption> displays a title above the table. When printing a long table across multiple pages, some browsers repeat the header and footer on each page.
Tag Omission Rules
The <tbody> element's opening and closing tags can both be omitted. When omitted, the browser automatically wraps any <tr> elements that don't belong to a <thead> or <tfoot> inside a <tbody>. This means the following two code examples are equivalent:
<!-- tbody omitted -->
<table border="1">
<tr>
<th>Name</th>
<th>Language</th>
</tr>
<tr>
<td>HTML</td>
<td>Markup</td>
</tr>
</table>
<!-- Browser interprets it as: -->
<table border="1">
<tbody>
<tr>
<th>Name</th>
<th>Language</th>
</tr>
<tr>
<td>HTML</td>
<td>Markup</td>
</tr>
</tbody>
</table>
<thead> and <tfoot>, on the other hand, cannot be omitted. Write them explicitly when you need row grouping.
For a more detailed explanation of table grouping, see thead / tbody / tfoot / caption.
Merging Cells (colspan / rowspan)
Use colspan to merge cells horizontally and rowspan to merge cells vertically. You must omit the corresponding <td> elements from subsequent rows or columns for each merged cell.
<table border="1">
<tr>
<th scope="col">Category</th>
<th scope="col">Language</th>
<th scope="col">Primary Use</th>
</tr>
<!-- rowspan="2" merges "Frontend" across 2 rows -->
<tr>
<td rowspan="2">Frontend</td>
<td>HTML</td>
<td>Page structure</td>
</tr>
<!-- The "Category" cell is omitted because it is covered by rowspan -->
<tr>
<td>CSS</td>
<td>Visual styling</td>
</tr>
<!-- rowspan="2" merges "Backend" across 2 rows -->
<tr>
<td rowspan="2">Backend</td>
<td>PHP</td>
<td>Server-side processing</td>
</tr>
<tr>
<td>Python</td>
<td>Data processing / AI</td>
</tr>
<tr>
<td>Full-stack</td>
<td>JavaScript</td>
<td>Both frontend & server</td>
</tr>
<!-- colspan="3" merges all 3 columns horizontally -->
<tr>
<td colspan="3">* See each language's dictionary page for details</td>
</tr>
</table>
The "Frontend" and "Backend" cells are merged vertically across 2 rows using rowspan="2". The last row is merged horizontally across all 3 columns using colspan="3", forming a single footnote cell. Notice how the corresponding <td> elements are omitted for each merged cell.
For a detailed explanation of cell merging, see colspan / rowspan.
Column Grouping (colgroup / col)
<colgroup> and <col> let you apply styles to entire columns at once. This eliminates the need to add classes to individual <td> or <th> elements, keeping your code clean.
<table border="1">
<caption>KOF Character List</caption>
<colgroup>
<!-- Column 1: Name (with background color) -->
<col style="background-color: #f0f0f0;">
<!-- Columns 2-3: grouped together -->
<col span="2">
</colgroup>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Affiliation</th>
<th scope="col">Fighting Style</th>
</tr>
</thead>
<tbody>
<tr>
<td>Yagami Iori</td>
<td>Yagami-ryu Old Fighting Style</td>
<td>Yagami-ryu + instinct</td>
</tr>
<tr>
<td>Kusanagi Kyo</td>
<td>Kusanagi-ryu Old Fighting Style</td>
<td>Kusanagi-ryu + freestyle martial arts</td>
</tr>
<tr>
<td>Terry Bogard</td>
<td>Southtown</td>
<td>Jeff's street fighting style</td>
</tr>
</tbody>
</table>
<col style="background-color: #f0f0f0;"> applies a background color to all cells in the first column (Name). <col span="2"> groups the next 2 columns together — here without any styles applied.
Styling Tables with CSS
While the HTML border="1" attribute can display borders, real-world websites often use CSS to style tables. Here are commonly used styling patterns:
/* Collapse borders into single lines (default is double) */
table {
border-collapse: collapse;
width: 100%;
}
/* Add borders and padding to cells */
th, td {
border: 1px solid #ccc;
padding: 8px 12px;
text-align: left;
}
/* Highlight the header row */
thead th {
background-color: #2c3e50;
color: #fff;
}
/* Striped rows */
tbody tr:nth-child(even) {
background-color: #f9f9f9;
}
/* Row highlight on hover */
tbody tr:hover {
background-color: #eaf2f8;
}
/* Footer row */
tfoot td {
background-color: #ecf0f1;
font-weight: bold;
}
/* Caption */
caption {
caption-side: top;
font-weight: bold;
margin-bottom: 8px;
}
Setting border-collapse: collapse; removes the gap between cells, producing single-line borders. Without it, each cell draws its own border, resulting in double lines. Using :nth-child(even) to add a background color to even rows makes the data easier to read.
Responsive Tables
Tables with many columns may not fit on a smartphone screen. Enabling horizontal scrolling allows the table content to be displayed without breaking the layout.
<!-- Wrap the table in a div to enable horizontal scrolling -->
<div style="overflow-x: auto;">
<table border="1">
<thead>
<tr>
<th>Name</th>
<th>Affiliation</th>
<th>Fighting Style</th>
<th>BGM</th>
<th>First Appearance</th>
</tr>
</thead>
<tbody>
<tr>
<td>Yagami Iori</td>
<td>Yagami-ryu Old Fighting Style</td>
<td>Yagami-ryu + instinct</td>
<td>Arashi no Saxophone 2</td>
<td>KOF '95</td>
</tr>
<tr>
<td>Kusanagi Kyo</td>
<td>Kusanagi-ryu Old Fighting Style</td>
<td>Kusanagi-ryu + freestyle martial arts</td>
<td>ESAKA</td>
<td>KOF '94</td>
</tr>
</tbody>
</table>
</div>
Simply wrapping the table in <div style="overflow-x: auto;"> shows a horizontal scrollbar when the table exceeds the screen width. Since the table HTML itself doesn't need any changes, this is the simplest responsive approach.
Common Mistakes
Forgetting to omit <td> cells when using rowspan
A cell with rowspan="2" occupies two rows, so the row it spans into must omit the <td> for that slot. Forgetting to do so shifts all subsequent columns out of alignment.
NG: Too many <td> elements cause misalignment.
<table border="1">
<tr>
<td rowspan="2">A (spans 2 rows)</td>
<td>B</td>
<td>C</td>
</tr>
<tr>
<!-- NG: the slot for A must be omitted, but a <td> is written anyway -->
<td>D (extra)</td>
<td>E</td>
<td>F</td>
</tr>
</table>
OK: Omit the <td> covered by rowspan.
<table border="1">
<tr>
<td rowspan="2">A (spans 2 rows)</td>
<td>B</td>
<td>C</td>
</tr>
<tr>
<!-- OK: the slot for A is omitted -->
<td>E</td>
<td>F</td>
</tr>
</table>
Forgetting border-collapse: collapse — getting double borders
Adding a CSS border to each cell alone creates double lines between adjacent cells. Setting border-collapse: collapse on the table merges neighboring borders into a single line.
NG: Double borders without border-collapse.
<!-- NG: border-collapse is missing, so adjacent cell borders are doubled -->
<table style="border: 1px solid #333;">
<tr>
<th style="border: 1px solid #333;">Name</th>
<th style="border: 1px solid #333;">Signature Move</th>
</tr>
<tr>
<td style="border: 1px solid #333;">Terry Bogard</td>
<td style="border: 1px solid #333;">Buster Wolf</td>
</tr>
</table>
OK: border-collapse: collapse produces single-line borders.
<!-- OK: border-collapse: collapse merges adjacent borders into one -->
<table style="border-collapse: collapse;">
<tr>
<th style="border: 1px solid #333;">Name</th>
<th style="border: 1px solid #333;">Signature Move</th>
</tr>
<tr>
<td style="border: 1px solid #333;">Terry Bogard</td>
<td style="border: 1px solid #333;">Buster Wolf</td>
</tr>
</table>
Writing <td> outside of <tr>
<td> elements must always be placed inside a <tr>. Writing <td> directly inside <table> produces invalid HTML. Browsers may silently repair the structure, but it can still cause unexpected layout issues. Always wrap cells in <tr> first.
NG: <td> written directly inside <table> without <tr>.
<table border="1"> <!-- NG: <td> placed directly without a wrapping <tr> --> <td>ID</td> <td>Name</td> <td>1</td> <td>Terry Bogard</td> </table>
OK: Wrap cells in <tr> to form rows.
<table border="1">
<tr>
<td>ID</td>
<td>Name</td>
</tr>
<tr>
<td>1</td>
<td>Terry Bogard</td>
</tr>
</table>
Notes
HTML tables are intended for displaying tabular data. In the past, tables were often used for page layout; CSS Flexbox or Grid are also options for layout. Tables are designed for presenting tabular data.
Adding the scope attribute to <th> elements explicitly tells assistive technologies such as screen readers which row or column the header applies to. Use scope="col" for column headers and scope="row" for row headers to improve accessibility.
For elements that give a table more semantic structure — <thead>, <tbody>, <tfoot>, and <caption> — see the "Table Grouping" section on this page and thead / tbody / tfoot / caption for further details. For merging cells, see the "Merging Cells" section on this page and colspan / rowspan.
Browser Compatibility
1 and later ○
1 and later ○
1 and later ○
8 ○
7 ○
6 ○
12.1 and later ○
11.1 and earlier ×
1 and later ○
Android Browser
4.4 and later ○
4 and earlier ×* Version data based on MDN.
If you find any errors or copyright issues, please contact us.