Free & open source — no account required

v1.2.5-PRICING-19
Database • Engineering Documentation

Markdown Mastery: Automating Data Documentation

This technical guide provides an in-depth analysis of the json to markdown table engine, best practices for implementation, and data security standards.

JSON to Markdown Table: Column Alignment, Escaping, GFM Features, and README Automation

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.

Live Example: Full-Featured JSON-to-Markdown Conversion

// 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 | ❌ |

Auto-Formatting: Padded Column Widths

// 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');
}

GFM Extensions: Task Lists and Footnotes

// 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 Actions: Auto-Update README Tables

// .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);

Best Practices for Production

  • Always escape pipe characters in cell values: A JSON string value containing | (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.
  • Right-align numeric columns: Numbers in left-aligned columns are nearly impossible to visually compare at a glance. Use ---: in the separator row for any column containing numbers. Markdown renderers correctly right-align text in those columns.
  • Use marker comments for auto-generated sections in README: Wrap auto-generated tables with HTML comment markers like <!-- 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.
  • Limit tables to 5-7 columns for readability: GitHub's default viewport displays ~90 characters of Markdown source. Tables wider than that force horizontal scrolling, which is nearly unusable on mobile. If you have more columns, split into two tables, or transpose the table (put fields as rows and records as columns).

FAQ

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.

Developer FAQ

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.