HTML tables are one of the most practical, everyday tools in web development. Pricing pages, comparison charts, schedules, data reports, sports scores — all of these are tables. This guide teaches you everything: how to build one from scratch, how to merge cells, how to style it beautifully with CSS, and how to make it work perfectly on mobile phones.
Every code example in this guide is copy-paste ready. Paste any example into our free HTML Editor Online Pro and see it rendered live in your browser instantly — no setup, no login needed.
An HTML table is a structured grid of rows and columns used to display data in an organized, readable format. It's one of the oldest and most useful elements in HTML — and one of the most misunderstood.
Before we get into how to build tables, let's get one important thing out of the way: HTML tables are for data, not for layout. In the early days of the web (before CSS), developers used tables to control the layout of entire pages — putting navigation in one column and content in another. That practice is completely outdated and wrong. Don't do it. Use CSS Flexbox or CSS Grid for layouts.
So when should you actually use an HTML table? Use a table when your data genuinely has a row-and-column relationship — when it would look like a spreadsheet in Excel. Good examples include:
Quick Rule: If your data would make sense in a spreadsheet like Excel or Google Sheets, it probably belongs in an HTML table. If it's just a list, use <ul>. If it's layout, use CSS Grid or Flexbox.
An HTML table is built using a specific set of tags that work together. Here is the most basic table you can create — three rows, three columns:
| Name | Subject | Score |
|---|---|---|
| Arjun | Mathematics | 92 |
| Priya | Science | 88 |
That's the foundation. Now let's understand what every single table tag does — because using them correctly is what separates well-structured HTML from messy code:
| Tag | Full Name | What it does |
|---|---|---|
<table> | Table | The outer container that wraps the entire table. Everything else goes inside this. |
<tr> | Table Row | Creates a horizontal row. Every row in your table needs a <tr> tag. |
<th> | Table Header | A header cell — bold and centered by default. Use for column titles. Has semantic meaning: tells browsers and screen readers this is a header. |
<td> | Table Data | A regular data cell. This is where your actual content goes in every non-header row. |
<thead> | Table Head | Groups the header row(s). Not required but strongly recommended — helps browsers, screen readers, and CSS target headers specifically. |
<tbody> | Table Body | Groups the main data rows. Makes the table structure clear and enables separate styling of body rows. |
<tfoot> | Table Footer | Groups footer rows — totals, summaries, notes at the bottom. Optional but semantically useful. |
<caption> | Table Caption | A title/description for the table. Appears above the table. Great for accessibility and SEO. |
<colgroup> | Column Group | Defines groups of columns for styling. Use with <col> to apply CSS to entire columns at once. |
The minimal table works — but a properly structured table uses <thead>, <tbody>, and <tfoot>. This is not just about being a perfectionist. These tags genuinely matter:
<thead> to announce column headers when reading data cells<thead> on every page automaticallythead, tbody, and tfoot separately for clean, maintainable stylingAlways add scope="col" to column header cells (<th>) and scope="row" to row header cells. This tells screen readers exactly which cells a header applies to — making your table fully accessible for visually impaired users.
Sometimes your table data doesn't fit neatly into equal-sized cells. You need one cell to span across multiple columns or rows. That's exactly what colspan and rowspan are for — and they're one of the trickier parts of HTML tables that trips up a lot of beginners.
colspan="n" makes a single cell take up the width of n columns. Think of it as merging cells horizontally — like merging cells in Excel.
| Student Exam Results — 2025 | ||
|---|---|---|
| Student | Theory | Practical |
| Rahul Verma | 78 | 45 |
| Anita Sharma | 91 | 48 |
rowspan="n" makes a single cell take up the height of n rows — merging cells vertically. This is very useful when a category label applies to multiple rows of data below it.
| Department | Employee | Role |
|---|---|---|
| Design | Meera Patel | UI Designer |
| Vikas Nair | UX Researcher | |
| Engineering | Arjun Singh | Frontend Dev |
| Priya Sharma | Backend Dev |
The most common rowspan/colspan mistake: When a cell spans multiple rows or columns, you must remove the corresponding cells from the rows/columns it covers. If you use rowspan="2", the next row should have one fewer <td> because that cell is already occupied by the rowspan. Forgetting this is why tables look broken.
An unstyled HTML table looks like something from 1998. The good news: with about 20 lines of CSS, you can make any table look genuinely professional. Here are three complete, ready-to-use table styles.
| Product | Category | Price | Stock |
|---|---|---|---|
| HTML Editor Pro | Developer Tool | Free | ✅ Live |
| CSS Framework | Frontend | ₹999/mo | ✅ Live |
| JS Library | JavaScript | ₹499/mo | ⏳ Beta |
| Design System | UI/UX | ₹1,499/mo | 🔜 Soon |
| Rank | Developer | Language | Points |
|---|---|---|---|
| 🥇 1 | Arjun Singh | JavaScript | 9,842 |
| 🥈 2 | Priya Sharma | Python | 9,210 |
| 🥉 3 | Rahul Verma | TypeScript | 8,775 |
| 4 | Meera Patel | React | 8,440 |
| 5 | Vikas Nair | Node.js | 8,120 |
Tables are the element most likely to break on mobile. A table with 6 columns that looks perfect on a desktop will overflow and get cut off on a phone screen. There are three good solutions depending on your situation.
The simplest approach: wrap your table in a <div> with overflow-x: auto. On small screens, the table will scroll horizontally rather than getting cut off. Users can swipe to see all the data.
For a more elegant mobile experience, you can completely restructure the table on small screens — stacking each row's data vertically instead of horizontally. This uses CSS and HTML data attributes to show the column label next to each value.
Test your table on mobile using our HTML Editor Online Pro. Switch to iPhone simulation mode to instantly see how your table behaves on a small screen — before you publish it.
Theory is great but real examples are better. Here are complete, ready-to-use HTML table templates for the most common use cases you'll encounter in real projects.
Copy any table code from this guide and paste it into HTML Editor Online Pro. See it rendered instantly. Switch to iPhone mode to check mobile layout. No signup, no download — it just works.
📊 Open HTML Editor Free →These are the mistakes that trip up almost every beginner working with HTML tables. Learn them now so you don't spend an hour debugging later.
Every row in a table must have the same total number of cells (accounting for any colspan/rowspan values). If row 1 has 3 columns but row 2 has 2, your table will look broken with misaligned columns. Count your cells carefully. If you use colspan="2" in one row, the next row still needs enough cells to fill all columns.
We already covered this, but it's worth repeating because it's so common. Tables are for data. Putting your navigation in a table cell and your content in another cell is wrong in 2025. Use CSS Flexbox (display: flex) or CSS Grid (display: grid) for layouts — always.
By default, HTML table cells have double borders — a border on the cell and a border on the table. Without border-collapse: collapse in your CSS, your table looks like it has thick double-line borders everywhere. Always add this:
Writing all rows directly inside <table> without <thead> and <tbody> works visually, but it's semantically wrong. Screen readers won't know which rows are headers. When printing a long table, headers won't repeat on each page. Always use the proper structural tags.
The scope attribute on <th> tells screen readers and assistive technology whether a header applies to its column (scope="col") or its row (scope="row"). Without it, your table is not fully accessible. It's one attribute, two seconds of work — always add it.
Q: How do I add a border to an HTML table?
The right way is with CSS, not the old HTML border attribute. Add this to your CSS: table, th, td { border: 1px solid #c8c8f0; } and table { border-collapse: collapse; }. The border-collapse: collapse merges the cell borders into single lines instead of double lines.
Q: How do I make alternate rows different colors (zebra stripes)?
Use the CSS :nth-child selector: tbody tr:nth-child(even) { background-color: #f5f5ff; }. This applies the background only to even-numbered rows (2nd, 4th, 6th...) creating a striped pattern that makes long tables much easier to read.
Q: What is the difference between th and td?<th> (Table Header) is a header cell — by default it's bold and centered. More importantly, it has semantic meaning: it tells browsers, search engines, and screen readers "this is a header that describes the data below/beside it." <td> (Table Data) is a regular data cell with no special semantic weight. Use <th> for column and row headers, <td> for everything else.
Q: How do I center text in a table cell?
Use CSS: td { text-align: center; } for horizontal centering. For vertical centering: td { vertical-align: middle; }. Avoid the old HTML align attribute — it's deprecated and shouldn't be used.
Q: Can I put images, buttons, and links inside table cells?
Yes, absolutely. A table cell <td> or <th> is just a container — you can put any HTML content inside it: images, links, buttons, forms, other elements. Many data tables include action buttons in the last column for edit/delete/view operations.
Q: How do I make a table sortable?
HTML alone can't sort tables — you need JavaScript for that. The simplest approach is to use a small JavaScript library like DataTables (free, open source). Or you can write a custom sort function using vanilla JavaScript that sorts the table rows when a header cell is clicked. This is a common intermediate JavaScript exercise.