Why Markdown Tables Beat Screenshots
Teams routinely paste screenshots of JSON data into docs and READMEs. Screenshots look polished for about a week — then the data drifts, the screenshot is stale, and nobody wants to re-screenshot the console because the formatting takes forever. A Markdown table is text: it is editable, grep-able, diffable, translatable, accessible to screen readers, and renders in dark mode and light mode without a separate asset. It is also one of the few things that renders the same in GitHub, GitLab, Notion, Obsidian, VS Code preview, Dropbox Paper, and almost every static site generator.
Example: API Response to README Table
You pulled a list of models from an API and want to document them in your README.
[
{ "model": "gpt-4o", "provider": "OpenAI", "context_tokens": 128000, "pricing_usd_per_1m": 2.50 },
{ "model": "claude-3.5-sonnet","provider": "Anthropic", "context_tokens": 200000, "pricing_usd_per_1m": 3.00 },
{ "model": "gemini-2.0-flash", "provider": "Google", "context_tokens": 1000000, "pricing_usd_per_1m": 0.075 }
]Output table (numeric columns auto right-aligned):
| model | provider | context_tokens | pricing_usd_per_1m |
|-------------------|-----------|---------------:|-------------------:|
| gpt-4o | OpenAI | 128000 | 2.50 |
| claude-3.5-sonnet | Anthropic | 200000 | 3.00 |
| gemini-2.0-flash | Google | 1000000 | 0.075 |Paste that straight into GitHub, GitLab, or Notion — renders instantly, no screenshot, no external image hosting.
GitHub Flavored Markdown Table Syntax
The GFM table format is minimal: three parts, separated by newlines.
- Header row: pipe-separated column names, with a leading and trailing pipe (optional but recommended for alignment).
- Separator row: at least three dashes per column, with optional colons for alignment:
:---left,:---:center,---:right. - Body rows: one per row, pipe-separated, same column count as the header.
Column width in the source is cosmetic — the renderer re-flows the table to fit the content. Padding to a consistent width makes the source readable in a plain text editor but is not required.
Pipe Escaping — The One Bug Everyone Hits
Pipes are the column separator. A literal pipe inside a cell breaks the table structure. Markdown escape with a backslash: \\|. The tool handles this for you — every pipe in a value gets escaped. Common cases where values contain pipes:
- URLs with query parameters that use pipes as separators.
- Regular expressions with alternation (
a|b|c). - Shell commands with pipelines (
cat x | grep y). - SQL IN clauses rendered as pipe-delimited.
Without escaping, a URL like ?x=a|b turns into a two-column spill that corrupts every row below.
Column Alignment Rules
GFM supports left, center, and right alignment per column. The tool picks sensibly by default:
- Numbers → right-aligned. Monetary values, counts, IDs. Easier to compare orders of magnitude at a glance.
- Dates (ISO 8601 strings) → left-aligned. They sort as strings cleanly, no alignment benefit.
- Booleans → center-aligned. Short values that look better centred.
- Everything else → left-aligned.
Override via the Columns panel: each column has a dropdown for alignment. The separator row changes accordingly: ---: for right, :---: for centre,:--- for left.
Handling Nested Objects and Arrays
Markdown tables are 2D. Nested JSON is not. Three options:
- JSON-stringify (default). A nested object becomes
{"x":1,"y":2}in the cell. Readable for small objects, unreadable for anything big. - Skip. Nested columns are dropped from the output. Clean, but loses data.
- Flatten first. Pre-flatten with the JSON Flattener tool so
profile.emailbecomes its own column. Best when the nested shape is consistent across rows.
Null and Empty Value Handling
JSON null and empty string "" are different values but usually look the same in a human-readable table. Pick a convention and apply it consistently:
- Empty cell (default). Clean, but ambiguous: is this missing or empty-string?
- Dash (—). The documentation convention: dash means "intentionally not applicable".
- Literal "null". Explicit, but adds noise.
Column Headers: Renaming and Formatting
Default column headers come from JSON keys verbatim — user_id,signed_up_at. For a README table that readers see, these look technical. The tool offers two header renames:
- Human-case:
user_id→User ID,signed_up_at→Signed Up At. Rough but automatic. - Explicit mapping: paste a CSV in the headers option (
id:ID,name:Full Name) to specify exact names.
Common Pitfalls
- Mixed-shape arrays. If row 1 has
{a, b}and row 2 has{b, c}, the column union isa, b, cand each row has gaps. The tool fills gaps with the configured null-cell. For large heterogeneous arrays, consider splitting into multiple tables. - Long values. A cell with 500 characters wraps in some renderers and breaks the visual table alignment. For long values, use a footnote-style link in the cell and put the full value under the table.
- Multi-line values.Markdown tables don't support cell newlines. The tool replaces newlines in values with
<br>, which GFM renders as a line break inside the cell on GitHub but not everywhere. - Right-to-left text. Hebrew, Arabic, and similar scripts render correctly in-cell but the overall column may need an explicit
<div dir="rtl">wrapper for alignment. Markdown table spec does not cover bidirectional text.
Where Markdown Tables Render
- GitHub — README, wiki, issues, PR descriptions, comments.
- GitLab — same places. Also in merge request diff comments.
- Bitbucket — README and wiki.
- Notion — paste a GFM table and Notion imports it as a native database.
- Obsidian — native support, no plugin.
- VS Code Markdown preview — native.
- Hugo, Jekyll, Hexo, 11ty, Astro, Next.js MDX — all GFM by default or via config.
- Dropbox Paper, Slab, Confluence (via plugin) — supported.
Related JSON Tools
- JSON Formatter — pretty-print and validate before converting.
- JSON to CSV — same shape, but comma-separated. Good for spreadsheets.
- JSON Flattener — flatten nested keys before conversion to get more informative columns.
- JSON Array Formatter — tidy the array before converting.