Table to Markdown

Copy a range out of a spreadsheet and the clipboard holds tab-separated text, which is exactly one character away from useless when the destination is a README or a GitHub issue. This reads the paste as it arrives, works out the separator, and emits the three formats that block usually needs to become.

Tab-separated (a spreadsheet copy) and comma-separated (CSV) are detected automatically. Quoted CSV fields are understood.
Table to Markdown — Convert a Spreadsheet Paste or CSV into Markdown, HTML and JSONBuildFigure

Why a spreadsheet paste arrives tab-separated

Copying a range in Excel, Google Sheets, LibreOffice or Numbers puts plain text on the clipboard with a tab between cells and a newline between rows. On screen a tab looks like a run of spaces, which is why splitting on spaces destroys the columns of anything containing a multi-word cell.

Detection here is deliberately simple: one tab anywhere in the input and the whole thing is read as tab-separated; otherwise whichever of comma, semicolon and pipe occurs most often wins. That guess is wrong occasionally — a semicolon-separated European CSV whose text fields are full of commas is the usual case — and the separator dropdown overrides it. Quoted CSV fields are handled properly for the comma and semicolon paths, so "London, SE1" stays one cell, and a doubled quote inside a quoted field comes through as a single quote.

Pipes inside cells, and other ways a Markdown table breaks

The pipe character is the column separator, so a cell containing one splits into two and every cell after it on that row shifts left. This tool escapes pipes inside cell content as \|, which every mainstream Markdown renderer understands, so text like up | down survives intact. Converting back the other way unescapes them again, so the round trip is clean.

The other structural failure is rows with different cell counts. A Markdown table needs every row to have the same number of columns, and a trailing empty cell often does not survive the copy out of the spreadsheet. Short rows are padded to the widest row and listed for you, capped at the first ten. Look at that list rather than dismissing it — an unexpectedly short row is frequently a real hole in the source data.

Merged cells and cell line breaks do not survive

Markdown table syntax has no concept of a merged cell. There is no colspan and no rowspan; a table is a plain grid. When you copy a range containing merged cells, the spreadsheet itself resolves the merge before it reaches the clipboard: the value lands in the first cell of the merged block and the cells it was spanning arrive empty. Those empty cells then get whatever your "empty cells" setting says — a hyphen by default — so a merged heading row typically comes out as one label followed by a line of hyphens.

There is no fix inside a Markdown table; the format cannot express it. Either unmerge in the source and repeat the value across the cells, or take the HTML output instead and add colspan attributes by hand, since HTML tables do support spanning.

Line breaks inside a cell have the same problem in weaker form. Markdown cells are single-line, so an embedded newline is converted to a <br> tag, which most renderers honour. The bigger hazard is upstream: a spreadsheet cell containing a line break is quoted on the clipboard and carries a real newline, so the paste can look like it has grown an extra row. Strip line breaks in the source before copying if the row count comes out wrong.

Alignment, empty cells and the JSON output

The second line of a Markdown table sets column alignment: :--- left, :---: centre, ---: right. Right-aligning numbers lines up the digits and is worth doing on any column of money or quantities, which is what the "numeric columns only" option is for — it right-aligns a column only when every non-empty cell in it parses as a number.

Empty cells default to a hyphen because an empty Markdown cell renders as a gap that makes the grid hard to read. If the table is going to be read back by a machine, choose "leave empty" instead, since a hyphen is indistinguishable from a real value. The JSON output ignores this setting entirely and always writes null for an empty cell, which is the honest representation.

With the header row in use, JSON comes out as an array of objects keyed by header name. Duplicate header names collide — the later column overwrites the earlier one — so rename the columns in the source or turn the header option off and take an array of arrays instead.

Questions people ask

My table has merged cells. What happens to them?

They are gone before the text reaches this page. Spreadsheets flatten merged cells when copying: the value goes into the top-left cell of the merged block and the remaining positions come through empty, which then get filled according to your empty-cell setting. Markdown tables cannot represent spanning at all, so there is nothing to preserve. If spanning matters, use the HTML output and add colspan or rowspan attributes yourself.

What happens to a pipe character inside my data?

It is escaped as \| in the Markdown output, so the table structure holds and the pipe displays as a pipe. Converting a Markdown table back to CSV reverses that, turning \| back into a plain pipe. The one place to be careful is if you have chosen pipe as the input separator — then a pipe inside a cell genuinely is a column break as far as the parser is concerned, and there is no quoting convention for pipe-separated text to rescue it.

Can I convert a Markdown table back into a spreadsheet?

Yes — set the direction to "Markdown table → CSV". Paste the table, including surrounding prose if it is easier, since lines without a pipe character are ignored. The alignment row is recognised and dropped, tables written without leading and trailing pipes are handled, and you get both CSV and a tab-separated version. Paste the tab-separated one into a spreadsheet cell and the columns split on their own.

How large a table can it take?

The input is capped at 400,000 characters. Well before that, a Markdown table of a few thousand rows stops being a sensible thing to put in a document — nobody scrolls it and the diff on every edit is enormous. Keep large data as CSV and link to it, or split it by section. Whatever the size, the conversion runs in the page and nothing is sent anywhere.

Related