MDX.org.ai
UI ComponentsmdxuiRenderers

@mdxui/json

Render MDX to JSON

@mdxui/json

Serialize MDX documents to JSON for APIs and storage.

Installation

npm install @mdxui/json

Usage

import { renderToJSON } from '@mdxui/json'
 
const mdx = `
---
title: Hello World
---
 
# Welcome
 
This is a **paragraph**.
`
 
const json = await renderToJSON(mdx)

Output Structure

interface JSONOutput {
  /** Frontmatter data */
  frontmatter: Record<string, unknown>
 
  /** Document content tree */
  content: ContentNode
 
  /** Table of contents */
  toc: TocEntry[]
 
  /** Export values */
  exports: Record<string, unknown>
}
 
interface ContentNode {
  type: string
  props?: Record<string, unknown>
  children?: (ContentNode | string)[]
}

Example Output

{
  "frontmatter": {
    "title": "Hello World"
  },
  "content": {
    "type": "root",
    "children": [
      {
        "type": "heading",
        "props": { "depth": 1, "id": "welcome" },
        "children": ["Welcome"]
      },
      {
        "type": "paragraph",
        "children": [
          "This is a ",
          { "type": "strong", "children": ["paragraph"] },
          "."
        ]
      }
    ]
  },
  "toc": [
    { "id": "welcome", "text": "Welcome", "depth": 1 }
  ],
  "exports": {}
}

Configuration

interface JSONRenderOptions {
  /** Include frontmatter */
  frontmatter?: boolean
 
  /** Include TOC */
  toc?: boolean
 
  /** Include exports */
  exports?: boolean
 
  /** Flatten text nodes */
  flattenText?: boolean
 
  /** Custom node transforms */
  transforms?: Record<string, Transform>
}

Features

Flat Text Nodes

const json = await renderToJSON(mdx, {
  flattenText: true,
})
 
// Output:
{
  "type": "paragraph",
  "text": "This is a paragraph.",
  "formatting": [
    { "type": "strong", "start": 10, "end": 19 }
  ]
}

Custom Transforms

const json = await renderToJSON(mdx, {
  transforms: {
    // Transform code blocks
    code: (node) => ({
      type: 'codeBlock',
      language: node.props.className?.replace('language-', ''),
      code: node.children[0],
    }),
 
    // Transform custom components
    Callout: (node) => ({
      type: 'callout',
      variant: node.props.type,
      content: node.children,
    }),
  },
})

Selective Output

// Content only
const contentOnly = await renderToJSON(mdx, {
  frontmatter: false,
  toc: false,
  exports: false,
})
 
// Metadata only
const metaOnly = await renderToJSON(mdx, {
  content: false,
  frontmatter: true,
  toc: true,
})

Use Cases

API Response

import { renderToJSON } from '@mdxui/json'
 
app.get('/api/content/:slug', async (req, res) => {
  const mdx = await getContent(req.params.slug)
  const json = await renderToJSON(mdx)
 
  res.json({
    ...json.frontmatter,
    content: json.content,
    toc: json.toc,
  })
})

Database Storage

import { renderToJSON } from '@mdxui/json'
import { db } from './database'
 
async function saveDocument(path: string, mdx: string) {
  const json = await renderToJSON(mdx)
 
  await db.documents.upsert({
    path,
    title: json.frontmatter.title,
    content: JSON.stringify(json.content),
    toc: JSON.stringify(json.toc),
    raw: mdx,
  })
}

Search Indexing

import { renderToJSON } from '@mdxui/json'
 
async function indexDocument(path: string, mdx: string) {
  const json = await renderToJSON(mdx, {
    flattenText: true,
  })
 
  return {
    path,
    title: json.frontmatter.title,
    description: json.frontmatter.description,
    text: extractText(json.content),
    headings: json.toc.map(t => t.text),
  }
}

Content Migration

import { renderToJSON } from '@mdxui/json'
 
// Convert MDX to portable format
const docs = await glob('docs/**/*.mdx')
 
const exported = await Promise.all(
  docs.map(async (path) => {
    const mdx = await readFile(path, 'utf-8')
    const json = await renderToJSON(mdx)
 
    return {
      path: path.replace('docs/', ''),
      ...json,
    }
  })
)
 
await writeFile('export.json', JSON.stringify(exported, null, 2))

Round-Trip

Convert JSON back to MDX:

import { renderToJSON, jsonToMDX } from '@mdxui/json'
 
// MDX -> JSON
const json = await renderToJSON(originalMDX)
 
// Modify JSON
json.frontmatter.updatedAt = new Date().toISOString()
 
// JSON -> MDX
const mdx = await jsonToMDX(json)

TypeScript

import { renderToJSON, type JSONOutput } from '@mdxui/json'
 
interface BlogPost {
  title: string
  date: string
  tags: string[]
}
 
const json = await renderToJSON<BlogPost>(mdx)
// json.frontmatter is typed as BlogPost

On this page