MDX.org.ai
Structured DatamdxldFormat Conversion

@mdxld/markdown

Bi-directional conversion between Objects and Markdown

@mdxld/markdown

Bi-directional conversion between JavaScript objects and Markdown. Uses convention-based layouts that automatically determine how objects render based on their shape.

Installation

pnpm add @mdxld/markdown

Quick Start

import { toMarkdown, fromMarkdown } from '@mdxld/markdown'
import { diff, applyExtract } from '@mdxld/extract' // path diff / apply, implemented once in @mdxld/diff
 
// Object to Markdown
const customer = {
  name: 'Customer',
  description: 'A customer entity',
  properties: [
    { name: 'id', type: 'string', required: true, description: 'Unique identifier' },
    { name: 'email', type: 'string', required: true, description: 'Email address' },
  ]
}
 
const markdown = toMarkdown(customer)

Output:

# Customer
 
A customer entity
 
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| id | string | ✓ | Unique identifier |
| email | string | ✓ | Email address |

API Reference

toMarkdown(object, options?)

Convert an object to Markdown using convention-based layout.

function toMarkdown<T extends object>(
  object: T,
  options?: ToMarkdownOptions
): string
 
interface ToMarkdownOptions {
  /** Starting heading depth (default: 1) */
  headingDepth?: number
  /** Include table of contents */
  toc?: boolean
  /** Use frontmatter for metadata */
  frontmatter?: boolean
  /** Table style: 'github' | 'simple' */
  tableStyle?: 'github' | 'simple'
  /** List style: 'dash' | 'asterisk' | 'plus' */
  listStyle?: 'dash' | 'asterisk' | 'plus'
}

Example with options:

const markdown = toMarkdown(entity, {
  headingDepth: 2,      // Start at ## instead of #
  tableStyle: 'github', // GFM tables
  listStyle: 'dash',    // Use - for lists
})

fromMarkdown(markdown, options?)

Parse Markdown back to an object using convention-based extraction.

function fromMarkdown<T = Record<string, unknown>>(
  markdown: string,
  options?: FromMarkdownOptions
): T
 
interface FromMarkdownOptions {
  /** Expected type for validation */
  type?: string
  /** Strict mode - throw on parse errors */
  strict?: boolean
}

Example:

const markdown = `# Customer
 
A customer entity
 
| Property | Type | Required |
|----------|------|----------|
| id | string | ✓ |
| email | string | ✓ |
`
 
const customer = fromMarkdown<Customer>(markdown)
// {
//   name: 'Customer',
//   description: 'A customer entity',
//   properties: [
//     { name: 'id', type: 'string', required: true },
//     { name: 'email', type: 'string', required: true },
//   ]
// }

parseTable(content) / renderTable(headers, rows, options?)

The one markdown table primitive: toMarkdown / fromMarkdown and the entity components of @mdxld/extract all read and write tables through it, so a table written by one is read by the other.

renderTable(['name', 'slug'], [{ name: 'JavaScript', slug: 'javascript' }])
// '| name | slug |\n|---|---|\n| JavaScript | javascript |'
 
parseTable('| name | slug |\n|---|---|\n| JavaScript | javascript |')
// { headers: ['name', 'slug'], rows: [{ name: 'JavaScript', slug: 'javascript' }] }

Diffing what comes back

@mdxld/markdown exports no diff / applyExtract: there is one diff implementation in the repo, @mdxld/diff (diffPaths, applyPaths, merge3wayObjects), and @mdxld/extract re-exports it as diff, applyExtract and the 3-way mergeExtract.

import { fromMarkdown } from '@mdxld/markdown'
import { diff, applyExtract } from '@mdxld/extract'
 
const changes = diff(original, fromMarkdown(edited))
// { added: {...}, modified: { 'age': { from: 30, to: 31 } }, removed: [], hasChanges: true }
 
const updated = applyExtract(original, fromMarkdown(edited), { arrayMerge: 'append' })

Layout Conventions

Objects are rendered based on their shape:

PropertyMarkdown Output
name# {name} heading
descriptionParagraph after heading
properties[]Table with columns
sections[]H2 subsections
items[]Bulleted list

Entity Example

const entity = {
  name: 'User',
  description: 'A user account',
  properties: [
    { name: 'id', type: 'string', required: true },
    { name: 'email', type: 'string', required: true },
    { name: 'role', type: 'string', enum: ['admin', 'user'] },
  ]
}
 
toMarkdown(entity)

Output:

# User
 
A user account
 
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| id | string | ✓ |  |
| email | string | ✓ |  |
| role | string |  |  |

Section Example

const doc = {
  name: 'Getting Started',
  sections: [
    { name: 'Installation', content: 'Run `npm install`' },
    { name: 'Usage', content: 'Import and use' },
  ]
}
 
toMarkdown(doc)

Output:

# Getting Started
 
## Installation
 
Run `npm install`
 
## Usage
 
Import and use

List Example

const list = {
  name: 'Features',
  items: ['Fast', 'Reliable', 'Scalable']
}
 
toMarkdown(list)

Output:

# Features
 
- Fast
- Reliable
- Scalable

Use Cases

Documentation Generation

import { toMarkdown } from '@mdxld/markdown'
 
const apiEndpoint = {
  name: 'Create User',
  description: 'Creates a new user account',
  properties: [
    { name: 'email', type: 'string', required: true, description: 'User email' },
    { name: 'password', type: 'string', required: true, description: 'User password' },
  ]
}
 
const docs = toMarkdown(apiEndpoint)
await Bun.write('docs/create-user.md', docs)

CMS Content Editing

import { toMarkdown, fromMarkdown } from '@mdxld/markdown'
import { diff, applyExtract } from '@mdxld/extract'
 
// Load structured content
const post = await db.BlogPost.get('hello-world')
 
// Render for editing
const markdown = toMarkdown(post)
 
// User edits in WYSIWYG editor...
const edited = await editor.edit(markdown)
 
// Extract changes
const extracted = fromMarkdown(edited)
const changes = diff(post, extracted)
 
if (changes.hasChanges) {
  // Review changes before saving
  console.log('Modified:', Object.keys(changes.modified))
 
  // Apply and save
  const updated = applyExtract(post, extracted)
  await db.BlogPost.update('hello-world', updated)
}

AI Content Improvement

import { toMarkdown, fromMarkdown } from '@mdxld/markdown'
 
// Render to markdown for AI
const markdown = toMarkdown(content)
 
// AI improves the content
const improved = await ai.improve(markdown, 'Make it more engaging')
 
// Extract changes back
const updated = fromMarkdown(improved)
PackageDescription
@mdxld/extractTemplate-based extraction
@mdxld/diffAdvanced diffing and patching
@mdxui/markdownStyled markdown output
mdxdbDocument storage