Table generator
Runs locallyReady.
Live preview
Cells are editableClick any cell to edit its text. The HTML output updates as you type.
HTML
CSS
What this generator does
Hand-writing table markup is repetitive and easy to get subtly wrong: an unclosed row, a missing scope attribute, a header row that ends up in tbody instead of thead. This tool builds the structure for you, then lets you edit the content in place so you do not have to touch the HTML at all unless you want to.
The output is deliberately plain. No framework classes, no inline styles, no dependencies — just semantic HTML and a small, readable stylesheet you can paste into any project. That makes it useful in places where a heavier component library would be overkill: a marketing page, an email template's companion web version, a documentation table, a static site, a README rendered by a Markdown processor that allows raw HTML.
Everything runs in your browser. There is no upload, no account, and no request made when you generate, edit or copy.
Table anatomy
An HTML table has four structural layers. Two are mandatory, two are optional but strongly recommended.
| Element | Purpose | Required? |
|---|---|---|
<table> | The container. Everything else lives inside it. | Yes |
<tr> | One row. Contains cells. | Yes |
<th> | A header cell. Bold and centred by default, and announced as a header by screen readers. | Usually |
<td> | A data cell. | Usually |
<caption> | A title that names the whole table. Must be the first child of table. | No, but recommended |
<thead> | Groups the header row or rows. | No, but recommended |
<tbody> | Groups the body rows. | No, but browsers imply it |
<tfoot> | Groups summary or footer rows. In HTML5 it must come after tbody. | No |
Why the sections matter
Without thead and tbody, a table is just a pile of rows. With them, several things start working:
- Screen readers can announce which section a cell belongs to and read the header when you move between data cells.
- Printing repeats the
theadon every page of a long table, so the reader does not lose the column names. - Styling becomes easier — you can target header cells with
thead thinstead of:first-childgymnastics. - Scripts can sort or filter
tbodywithout touching the header or footer.
Making the table accessible
A table that looks fine can still be unusable with a screen reader. Three attributes do most of the work, and the generator sets the first automatically.
Caption
The caption element gives the table an accessible name. Without one, a screen reader may announce it as "table, 5 rows, 4 columns" with no indication of what it contains. Put the caption on every table that carries real data — it is not decoration, it is the table's identity.
scope
Each th in the header row gets scope="col". This tells assistive technology that the cell labels the column below it. A table with row headers uses scope="row" on the first cell of each row instead. The generator emits column scope because its default data model is column-headed; if your own data has row headers, edit the output to swap them.
Headers attribute
For a table with an irregular structure, or one where merged cells break the simple row/column relationship, each td can point at its header cells by id:
<th id="price" scope="col">Price</th> ... <td headers="price">£19.99</td>
This is rarely necessary in a simple grid, and the generator does not emit it. It matters when you have two sets of headers, or merged cells that would otherwise confuse the announcement.
What to check before publishing
- Does the table have a
caption? If not, add one. - Do the header cells have
scope? - Is there any content in a cell that only makes sense visually — a colour coding, an arrow, a decorative icon? Add a text equivalent.
- At a narrow viewport, does the table scroll sideways rather than squash to unreadable widths? The responsive wrapper handles this.
When not to use a table
The table element is for tabular data: information where the row and column positions carry meaning, such as a price list, a comparison, a schedule, or a set of statistics. It is not a layout tool.
Why the old "tables for layout" approach is bad
- Screen readers announce it as a data table, so users are told "table, 12 rows" for what is really a header, a sidebar and a footer. Navigating becomes confusing.
- It does not reflow. A three-column table stays a three-column table at 320 px wide, producing horizontal scrolling or truncated content.
- It fights your CSS. Flexbox and grid are designed for layout; using a table means overriding default cell display, border collapse and vertical alignment on every element.
- It is bad for SEO. Content wrapped in a maze of tables is harder for a crawler to interpret, and internal link hierarchy gets lost.
Use instead
CSS Grid for two-dimensional layout, flexbox for one-dimensional rows and columns, and ordinary semantic elements — header, nav, main, aside, footer, section, article — for structure. The only case where a table still makes sense outside of data is an actual email template, because email clients have poor support for modern CSS layout. Even there, prefer a simple single-column design and reserve tables for genuinely tabular content.
Colspan and rowspan
The generator produces a rectangular grid. Merging cells is deliberately out of scope, because a merge changes the grid in ways a rows-and-columns input cannot express — a cell that spans two columns removes one cell from the row, and a cell that spans two rows affects the row below it.
To add merged cells to the output, edit the HTML directly. Two attributes do the work.
colspan
Makes a cell stretch horizontally across several columns. The other cells in the same row must be removed to keep the total count correct.
<tr> <th scope="col" colspan="2">Name</th> <th scope="col">Role</th> </tr> <tr> <td colspan="2">Ada Lovelace</td> <td>Engineer</td> </tr>
rowspan
Makes a cell stretch vertically. The rows below lose a cell at that position.
<tr> <td rowspan="2">Engineering</td> <td>Ada Lovelace</td> </tr> <tr> <!-- the first cell is already occupied by the rowspan above --> <td>Grace Hopper</td> </tr>
Styling notes
The exported CSS uses rgba(128, 128, 128, …) rather than fixed hex colours. A mid-grey at low alpha reads as a subtle line or tint on a white background and as a subtle lightening on a dark one, so the same stylesheet works on either without editing. If you want exact brand colours, replace the four rgba values with your own tokens.
What each rule does
border-collapse: collapse— single-pixel borders shared between adjacent cells, instead of the default doubled line.thead thbackground — a tint that separates the header from the body without looking like a button.tbody tr:nth-child(even)— the striped-row effect, optional and easy to remove.tbody tr:hover— a highlight when the pointer is over a row, useful for wide tables where the eye can drift.- The
@media (max-width: 640px)block reduces padding and font size so the table stays readable on a phone without horizontal scrolling for small tables.
The responsive wrapper
Wrapping the table in <div class="table-responsive"> with overflow-x: auto is the simplest way to keep a wide table usable on a narrow screen. The table stays at its natural width and the container scrolls sideways. It is not a perfect solution — horizontal scrolling is annoying — but it is far better than a table that squashes into unreadable columns or overflows the viewport and breaks the page layout.
What this generator cannot do
- No merged cells through the interface.
colspanandrowspanneed manual editing; see the section above for worked examples. - No row headers. The default structure labels columns, not rows. If your data is a two-dimensional matrix where each row also has a label, edit the first cell of each row into a
th scope="row". - No sorting, filtering or pagination. The output is static markup. Adding interactivity needs JavaScript of your own.
- No styling presets. One neutral stylesheet, no theme gallery. Design choices belong in your own CSS.
- No Markdown output. The output is HTML. Convert separately if you are targeting a Markdown file that only supports pipe tables, which cannot express captions or scoped headers anyway.
- No data import from CSV files. Paste from a spreadsheet works (tabs and newlines); uploading a
.csvfile does not. - Content is stored only in the page's memory. Reload the tab and your edits are gone. Copy or download before closing.
Frequently asked questions
Is the generated table accessible?
The markup is as accessible as the options you enable. With the header row on, header cells are th elements inside a thead and each carries scope="col", which is what lets a screen reader announce the column name when a data cell is read. With the caption option on, the caption element gives the table an accessible name. Turning both off produces valid HTML but an unlabelled table, which is hard to navigate with assistive technology.
What do the row and column counts mean?
Body rows means rows in the tbody. If you enable the header row, one extra row appears above it in a thead. If you enable the footer row, one extra row appears below it in a tfoot. So five body rows with both options enabled produces seven visible rows and a cleaner semantic structure than treating one of the body rows as a header.
Will the generated CSS work on a dark website?
Yes. The CSS uses rgba(128, 128, 128, …) for borders, header backgrounds and hover states rather than hard-coded hex values. A mid-grey with low alpha reads as a subtle line or tint on a white background and as a subtle lightening on a dark one, so the same stylesheet works on either without editing. If you want exact colours, override the four rgba values with your own theme tokens.
Can I edit the cell contents?
Yes. Every cell in the live preview is editable, so click into it and type. The HTML output updates as you edit, and your changes survive option changes. If you change the row or column count, content that no longer fits is discarded, because the coordinates have changed.
Can I paste data from Excel or Google Sheets?
Yes. Copy the cells in your spreadsheet and paste them into the data box. Tabs separate columns and newlines separate rows, which is exactly what a spreadsheet puts on the clipboard. The row and column counts update to match the pasted data. If the first row is meant to be headers, leave the header-row option on.
Does it support colspan and rowspan?
Not through the interface, because merging cells changes the grid in ways that a simple row-and-column input cannot express. The generated markup is the correct starting point, and the page shows a worked example of adding colspan and rowspan by hand. For anything more than one or two merged cells, editing the HTML directly is faster than a visual merge tool.
Should I use a table for page layout?
No. Tables are for tabular data — rows and columns where the position of a cell carries meaning, such as a price list or a schedule. Using a table to position content on a page breaks screen-reader navigation, does not reflow on mobile, and is not how modern CSS layout works. Use flexbox or grid for layout and reserve the table element for data.
Is anything uploaded when I use this tool?
No. The table is built in JavaScript on your device. There is no API call and no analytics event containing your data. You can confirm this in the Network tab: paste data into the box and watch that no request is made. The page works with the connection off once it has loaded.
About this tool
Editorial and verification standards are in the editorial policy. Data handling is in the privacy policy.