JSON to Markdown Table
Paste an array, get a table you can drop straight into a README
JSON
Markdown table
Your table appears here
Paste a JSON array on the left. The Markdown is built as you type.
Getting a payload into a README without retyping it
You have an array of records — the response from an endpoint you are documenting, a config dump, a list of feature flags — and it needs to appear in a README, a pull request description or a wiki page. Markdown has a perfectly good table syntax for it. What it does not have is any way to get your data into that syntax except by hand, and doing that by hand for twenty rows is how an afternoon disappears.
Paste the array on the left and the table is on the right, ready to copy. Nested objects flatten to dot-path columns, so device.model becomes its own column rather than a cell full of braces. Columns are collected across every record, not just the first one — if the fifth object carries a field the first four did not, it still gets a column.
Two characters in your data can break a Markdown table, and both turn up in real payloads. A literal | ends the cell early, quietly shifting every column after it one place to the left; it renders without an error, so you find out when someone points at the wrong-looking row. A newline ends the whole row. Pipes are escaped and newlines become <br> here, because there is no such thing as a multi-line cell — the GFM tables extension defines a row as a single line and that is the end of it.
One thing worth knowing before you paste this anywhere: tables are not part of core Markdown. The CommonMark specification has no table syntax at all. They come from GitHub's extension, which is why the same table renders on GitHub, GitLab and most static site generators, and renders as literal pipes in a strict CommonMark renderer. If a table has ever come out as a wall of vertical bars, that is why.
How to use it
- Paste your JSON – An array of objects is the usual shape. A lone object works too and becomes a single row, and a response wrapped as {"results": [...]} is read through to the array inside it.
- Read the table on the right – It updates as you type. The header row is the union of every key in the document, in the order those keys first appeared.
- Pick an alignment – Left, centre or right. This writes the colons into the delimiter row — the second line of the table — which is the only place Markdown records alignment.
- Decide whether to line the columns up – Padding makes no difference to how the table renders. It makes a large difference to whether you can read the raw file afterwards, which is where these tables live.
- Copy it, or download a .md file – Copy is what you want for a pull request comment. Download gives you a file to commit.
Pro tip: if a value contains a stack trace or an address, it arrives as one cell with <br> between the lines. That is the only way a Markdown table can hold it — but for anything longer than two or three lines, a table cell is the wrong container. Link out to a code block below the table instead.
A worked example
Three SIM provisioning records. Note the ICCID: it is 19 digits, past the point where a JavaScript number keeps every one of them, and it comes out intact. Note also that only the third record carries a "note" field — it still gets a column.
[
{
"msisdn": "447700900112",
"iccid": 8944501012345678901,
"plan": "Unlimited 5G",
"device": { "model": "Pixel 9" }
},
{
"msisdn": "447700900204",
"iccid": 8944501055512340987,
"plan": "IoT Metered 2GB",
"device": { "model": "Quectel BG95" },
"note": "fleet tracker, EU only"
}
]| msisdn | iccid | plan | device.model | note | | ------------ | ------------------- | --------------- | ------------ | ---------------------- | | 447700900112 | 8944501012345678901 | Unlimited 5G | Pixel 9 | | | 447700900204 | 8944501055512340987 | IoT Metered 2GB | Quectel BG95 | fleet tracker, EU only |
Where this actually gets used
Documenting an API response in a README
You are describing an endpoint and want to show the fields it returns. Paste one real response, get the table, and the documentation now matches the payload exactly instead of matching what you remembered about the payload. GitHub's own guidance on organizing information with tables is worth a read if you are laying out anything more involved than this.
Pasting evidence into a pull request
Reviewers do not want to read a minified array in a comment box. A table of the eight rows that changed behaviour is read at a glance, and it survives in the PR history in a way that a screenshot does not — searchable, diffable, and still legible in a plain-text email notification.
Turning a config dump into a wiki page
Feature flags, environment settings, a service registry — anything you already hold as JSON and someone non-technical needs to read. Convert once, paste into the wiki, and re-run it when the config changes rather than editing cells by hand. If the destination renderer is unfamiliar, opening the result in an online markdown viewer first is a quick way to catch a flavour difference before you publish it.
What it handles
- Columns from every record – a field that only appears halfway down the file still gets a column
- Nested objects flattened –
device.modelas a column, not a cell full of JSON - Pipes escaped – a
|in a value cannot shift the rest of the row - Newlines converted – multi-line values become one cell with
<br>between the lines - Long integers kept exact – a 19-digit identifier prints all 19 digits
- Source alignment – padded columns so the raw Markdown is still readable and hand-editable
- Nothing leaves the browser – the conversion runs on your machine, with no upload
Questions people actually ask
Why is my table rendering as a row of vertical bars?
The renderer you are pasting into does not support tables. They are a GitHub extension rather than part of core Markdown, so a strict CommonMark renderer shows the pipes literally. GitHub, GitLab, Bitbucket, Obsidian and most static site generators are fine; a few minimal renderers and some comment boxes are not.
Can a cell hold more than one line?
Not literally — a newline ends the row, so there is no multi-line cell in the syntax. Line breaks become <br>, which every renderer that supports tables also understands. For a long value, a table is the wrong shape; put a reference in the cell and the full text in a code block underneath.
What happens to arrays inside my objects?
They stay in one cell as compact JSON. Spreading a list across bearers.0.qci, bearers.1.qci and so on would make the column count depend on whichever record happened to be longest, and produce a header row nobody can read. One honest cell is better than forty misleading ones.
Does padding change what gets rendered?
No. Renderers trim the whitespace inside cells, so a padded and an unpadded table produce identical output. Padding is for the human who opens the file next — a ragged table is hard to read and easy to break when hand-editing, and these files get hand-edited.
My JSON is a single object, not an array. Will it work?
Yes, it becomes a one-row table. And if your object is a wrapper — {"results": [ ... ]}, the shape a great many APIs return — the array inside is used and a note tells you which key it came from, so nothing happens silently.
Related tools
Worth reading
- Markdown Guide: extended syntax – Tables, footnotes and the other things core Markdown leaves out
- MDN: JSON.parse() – Including why it rounds long integers, which is the bug this site cares about most