Free & open source — no account required
Free & open source — no account required
This technical guide provides an in-depth analysis of the json to markdown table engine, best practices for implementation, and data security standards.
Generating Markdown tables from JSON means handling more than a simple pipe-and-dash layout: values containing pipe characters break table structure, values with newlines can't be expressed in standard Markdown tables, numeric columns should right-align for visual comparison, headers derived from JSON keys need snake_case-to-Title-Case conversion, and long values in any cell force all cells in that column to be the same padded width for readability. This guide covers the complete generation pipeline, including proper escaping, GitHub Flavored Markdown (GFM) extensions, and a GitHub Actions workflow for auto-updating docs.
// Input JSON: API performance benchmarks
const data = [
{ endpoint: "GET /users", p50_ms: 12, p95_ms: 48, p99_ms: 120, error_rate: 0.001, stable: true },
{ endpoint: "POST /orders", p50_ms: 38, p95_ms: 142, p99_ms: 310, error_rate: 0.003, stable: true },
{ endpoint: "GET /reports/export", p50_ms: 2400, p95_ms: 8100, p99_ms: 15000,error_rate: 0.012, stable: false },
];
// Header configuration: display name + alignment
const columns = [
{ key: 'endpoint', label: 'Endpoint', align: 'left' },
{ key: 'p50_ms', label: 'P50 (ms)', align: 'right' },
{ key: 'p95_ms', label: 'P95 (ms)', align: 'right' },
{ key: 'p99_ms', label: 'P99 (ms)', align: 'right' },
{ key: 'error_rate', label: 'Error Rate', align: 'right' },
{ key: 'stable', label: 'Stable', align: 'center'},
];
type Align = 'left' | 'center' | 'right';
function escapeCell(value: unknown): string {
if (value === null || value === undefined) return '';
const str = typeof value === 'boolean'
? value ? '✅' : '❌'
: String(value);
return str
.replace(/\\/g, '\\\\') // backslash first
.replace(/\|/g, '\\|') // pipe — would break the column
.replace(/\n/g, '<br>'); // newlines not supported in Markdown tables
}
function alignSep(align: Align): string {
if (align === 'left') return ':---';
if (align === 'right') return '---:';
if (align === 'center') return ':---:';
return '---';
}
function jsonToMarkdownTable(
rows: Record<string, unknown>[],
cols: typeof columns
): string {
const header = '| ' + cols.map(c => c.label).join(' | ') + ' |';
const sep = '| ' + cols.map(c => alignSep(c.align as Align)).join(' | ') + ' |';
const body = rows.map(row =>
'| ' + cols.map(c => escapeCell(row[c.key])).join(' | ') + ' |'
);
return [header, sep, ...body].join('\n');
}
console.log(jsonToMarkdownTable(data, columns));
// Output:
// | Endpoint | P50 (ms) | P95 (ms) | P99 (ms) | Error Rate | Stable |
// | :--- | ---: | ---: | ---: | ---: | :---: |
// | GET /users | 12 | 48 | 120 | 0.001 | ✅ |
// | POST /orders | 38 | 142 | 310 | 0.003 | ✅ |
// | GET /reports/export | 2400 | 8100 | 15000 | 0.012 | ❌ |
// Pretty-printed tables align columns for raw Markdown readability
function padded(value: string, width: number, align: Align): string {
const v = value.padEnd(width);
if (align === 'right') return value.padStart(width);
if (align === 'center') {
const l = Math.floor((width - value.length) / 2);
return ' '.repeat(l) + value + ' '.repeat(width - value.length - l);
}
return v;
}
function jsonToMarkdownTableFormatted(
rows: Record<string, unknown>[],
cols: typeof columns
): string {
// Calculate max width for each column
const widths = cols.map(col => {
const values = [
col.label,
...rows.map(r => escapeCell(r[col.key])),
];
return Math.max(...values.map(v => v.length));
});
const renderRow = (cells: string[]) =>
'| ' + cells.map((c, i) => padded(c, widths[i], cols[i].align as Align)).join(' | ') + ' |';
const sep = '| ' + cols.map((c, i) => {
const d = alignSep(c.align as Align);
return d.startsWith(':') && d.endsWith(':')
? ':' + '-'.repeat(widths[i] - 2) + ':'
: d.endsWith(':')
? '-'.repeat(widths[i] - 1) + ':'
: ':' + '-'.repeat(widths[i] - 1);
}).join(' | ') + ' |';
return [
renderRow(cols.map(c => c.label)),
sep,
...rows.map(r => renderRow(cols.map(c => escapeCell(r[c.key])))),
].join('\n');
}
// GitHub Flavored Markdown allows task list checkboxes in table cells
// Use for feature matrix tables
const features = [
{ feature: "OAuth2 Login", free: true, pro: true, enterprise: true },
{ feature: "Audit Logging", free: false, pro: false, enterprise: true },
{ feature: "SSO / SAML", free: false, pro: false, enterprise: true },
{ feature: "API Rate Limits", free: "100/h", pro: "10k/h", enterprise: "Unlimited" },
];
// Map boolean to GFM task list (renders as checkboxes in GitHub)
function boolToCheckbox(v: unknown): string {
if (v === true) return '- [x]';
if (v === false) return '- [ ]';
return String(v);
}
// Footnotes in Markdown tables (using reference links)
const table = `
| Feature | Free | Pro | Enterprise |
| :--- | :---: | :---: | :---: |
| OAuth2 Login | - [x] | - [x] | - [x] |
| Audit Logging | - [ ] | - [ ] | - [x] |
| SSO / SAML | - [ ] | - [ ] | - [x] [^sso] |
| API Rate Limits | 100/h | 10k/h | Unlimited |
[^sso]: Supports SAML 2.0 and OIDC. Contact sales for setup.
`;
// .github/workflows/update-docs.yml
name: Update README Tables
on:
push:
paths: ['data/**/*.json', 'scripts/generate-tables.ts']
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npx ts-node scripts/generate-tables.ts
- name: Commit if changed
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add README.md docs/
git diff --staged --quiet || git commit -m "docs: regenerate tables from JSON data"
git push
// scripts/generate-tables.ts
import { readFileSync, writeFileSync } from 'fs';
const benchmarks = JSON.parse(readFileSync('./data/benchmarks.json', 'utf-8'));
const table = jsonToMarkdownTable(benchmarks, columns);
let readme = readFileSync('./README.md', 'utf-8');
// Replace content between marker comments
readme = readme.replace(
/(<!-- TABLE:benchmarks -->)[\s\S]*?(<!-- \/TABLE:benchmarks -->)/,
`$1\n\n${table}\n\n$2`
);
writeFileSync('./README.md', readme);
| (common in URLs, regex patterns, and CLI output) silently corrupts the table structure — subsequent columns shift or disappear. Replace | with \| in every cell value before rendering.---: in the separator row for any column containing numbers. Markdown renderers correctly right-align text in those columns.<!-- TABLE:benchmarks --> and <!-- /TABLE:benchmarks -->. A CI script replaces the content between the markers, preserving the rest of the README. Without markers, the script must parse Markdown structure which is fragile.Q: How do I handle nested JSON objects or arrays in table cells?
A: Flatten them. For arrays: join with commas (["a","b","c"] → a, b, c) or semicolons. For nested objects: either pick specific nested fields as separate columns, or use a compact JSON representation ({"host":"db","port":5432}). Full pretty-printed JSON in a table cell is always unreadable.
Q: Can Markdown tables span multiple pages or be sortable?
A: Standard GFM tables are static and can't be sorted. For sortable tables in GitHub wikis or documentation sites, use an HTML <table> with a JavaScript sorter, or use a documentation framework that supports interactive tables (Docusaurus, MkDocs with extensions). If your data needs sorting, a table in a README is the wrong medium.
Q: How do I generate a wide table without horizontal scrolling?
A: Use a transposed layout: put field names as the left column and one record per subsequent column. This often works better when you have many fields per record but few records. Alternatively, use collapsible sections with HTML <details> tags to hide verbose columns by default.
Is the processing local-only?
Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.
Can I use this for enterprise projects?
Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.