HTML Table Generator

Build an HTML table visually, paste from Excel/Sheets, add or remove rows and columns, merge cells, pick a styling preset, then copy clean HTML.

🌐 Español

Click a cell to select it, shift-click another cell to select a rectangle, then Merge selected cells. Copy cells from Excel or Google Sheets and paste anywhere in the grid to replace it with your pasted data.

Preview

HTML

🔒 Private by design: everything runs locally in your browser and never uploaded to any server.

Where the time really goes when you write a table by hand

Typing <table>, <tr> and <td> is tedious but not hard. The part that eats an afternoon is arithmetic. Every row in an HTML table has to account for the same number of columns, and a rowspan in row two silently steals a slot from rows three and four, so the moment cells start spanning you are keeping a mental ledger of how many <td> elements each row still needs. Get one wrong and the browser does not complain; it just renders a table with a step in the side of it, and you go looking for the row that lied about its width.

A grid editor removes the ledger. You select the rectangle you want joined and the spans are computed for you, along with the corresponding removal of the cells that rectangle absorbed. What you see in the preview is the markup you are about to copy.

The grid opens as a three by three, with the first row already marked as a header, the striped preset selected and the responsive wrapper on, which is the shape most people want. Add rows and columns from the toolbar; remove them with the small × buttons along the top edge and down the left side, which stay disabled once you are down to a single row or column. Click a cell to select it, shift-click another to extend the selection into a rectangle. There is no drag-select and no undo history, so the escape hatch for a mess is Clear table, which resets to a fresh three by three.

How merges are stored, and the one restriction that follows

Internally the table is a plain two-dimensional array of cell text plus a separate flat list of merge rectangles. Each merge records only its top-left anchor and how many rows and columns it spans, and every other cell inside that rectangle is treated as covered: its text is still stored, but no <td> is emitted for it. That is what makes the output valid by construction, because “which cells are hidden” is always derived from the merge list rather than tracked as a second flag that could drift out of step with it.

Keeping the covered text also means unmerging is genuinely reversible. Break a merge apart and every cell inside it comes back with whatever it held before, which is not true of most spreadsheet applications.

The restriction that falls out of this design is that merges cannot overlap or nest. Selecting a rectangle that touches an existing merged block is refused with a message rather than half-dissolving the old one, because supporting a merge of merges would need a genuinely different data structure to serve a case this tool does not need. To grow a merged block, unmerge it and merge the bigger rectangle. Deleting a row or column that a merge spans is handled the same honest way: the merge dissolves into ordinary cells first, then the deletion happens, and merges positioned after the deleted line shift to stay correct.

Header cells, striped rows, and the two Copy buttons

The two header checkboxes are independent, and both do exactly one thing: they switch the first row’s cells, or the first column’s cells, from <td> to <th>. Turn on both and the top-left corner is a <th> as well. Note what is not produced, since it occasionally matters: there is no <thead> or <tbody> element and no scope attribute on the header cells, so if you are shipping this into a context with strict accessibility review you will want to add those yourself. For a merged block, header status is decided by its anchor cell’s own position rather than by the whole rectangle’s overlap.

Five presets are available: Plain, Minimal, Striped rows, Bordered and Dark mode. Striped rows counts body rows only, so the shading starts with the second real data row and is not thrown off by the header sitting above it. The responsive checkbox wraps the finished table in a <div style="overflow-x:auto">, which is what stops a twelve-column table from blowing out the layout of a blog post on a phone.

Getting the result out works three ways, and the difference between the first two catches people. Copy HTML puts the markup on your clipboard as text, for pasting into a template or an editor. Copy rendered table puts it on the clipboard as text/html, so pasting into Google Docs, Word or an email composer produces an actual formatted table instead of a wall of tags. Download .html saves the markup as a file.

Put together, building one table runs like this:

  1. Size the grid with + Add row and + Add column, or paste a block copied from Excel or Google Sheets anywhere in the grid to replace it wholesale.
  2. Type into the cells. Click one cell, then shift-click another, to select a rectangle.
  3. With a rectangle selected, press Merge selected cells; Unmerge puts a merged block back with every cell’s text intact.
  4. Set the header checkboxes, pick a style preset, and decide on the responsive wrapper, watching the live preview.
  5. Take the result with Copy HTML, Copy rendered table or Download .html, depending on where it is going.

Grid editor, JSON, or Markdown

Three tools here overlap and the right one depends on what you already have. If your data is a JSON array, JSON to HTML Table skips the grid entirely and lets you choose which keys become columns; it shares these exact styling presets, so switching between the two changes nothing about the look. If the destination is a README, a wiki or a chat message, Markdown Table Generator uses the same grid-editing approach and emits pipe syntax instead. And if the table is really the whole deliverable, download the .html here and run it through HTML to PDF.

Everything runs in the tab, which is worth a sentence given what tends to get pasted into a grid like this: pricing not yet announced, salary bands, client lists. The clipboard data never leaves the page.

See it in action

Screenshot of the HTML Table Generator tool with an editable grid with add-row, add-column and cell merging - click a cell, shift-click another to select a rectangle, then merge - above the HTML it generates, and cells can be pasted straight in from Excel or Google Sheets
HTML Table Generator mid-process: an editable grid with add-row, add-column and cell merging - click a cell, shift-click another to select a rectangle, then merge - above the HTML it generates, and cells can be pasted straight in from Excel or Google Sheets.
Diagram: where the work happens on a SysFenix page that has no file input at all: the tool arrives as ordinary JavaScript inside the page, works the answer out on your own device and renders it in place, so the upload, queue and server-side record a typical online tool needs never happen
Where the work happens on a SysFenix page that has no file input at all: the tool arrives as ordinary JavaScript inside the page, works the answer out on your own device and renders it in place, so the upload, queue and server-side record a typical online tool needs never happen.

Frequently asked questions

Why does it say my cells are already part of a merge?

Merges are stored as flat rectangles rather than as a tree, so one cannot contain another, and a selection overlapping an existing merged block is refused instead of being silently nested or split. Unmerge the existing block first, which restores every cell inside it with its text intact, then select the wider rectangle and merge that. Growing a merge is therefore two clicks rather than one, which is the price of markup that is always valid.

What happens if I delete a row that a merged cell spans?

The merge dissolves back into ordinary individual cells first, then the row or column is removed. Shrinking the rectangle instead would leave a rowspan pointing at cells that no longer line up under it, and every merge anchored below or right of the deleted line has its position shifted to compensate.

Can I paste a range straight out of Excel or Google Sheets?

Yes, click any cell and press Ctrl+V or Cmd+V. Spreadsheet clipboard data is tab-separated with one line per row, and a field containing a tab, a quote or its own line break arrives wrapped in double quotes; the parser handles that quoting properly, so a multi-line cell comes through as one cell. The paste replaces the whole grid and clears any merges, and ragged rows are padded out to the widest one.

Is it safe to paste cell text that contains HTML?

Yes. Every cell's text is escaped before it reaches the output, so a value like an alert script tag becomes visible inert text rather than real markup. That also protects the table itself, since an unescaped angle bracket in a cell would otherwise break the surrounding structure.

Why are the styles inline instead of a CSS class?

Because the output has to survive being pasted somewhere you do not control. Email clients strip style blocks outright, and plenty of CMS and wiki editors do the same, so a table styled by a class would arrive unstyled. Inline style attributes travel with the markup. The five presets are literally the same definitions used by the JSON to HTML Table converter, imported rather than copied, so the same preset looks identical in both tools.

Does the downloaded file include a full HTML page?

No, the .html file contains the table markup on its own, with no doctype, head or body wrapper. Browsers will still display it, and it pastes cleanly into a template, but if you need a standalone document add your own wrapper around it.

Related tools