API Reference

Render API

Convert a Comark AST back to markdown, preserving frontmatter and component syntax.

renderMarkdown(tree, options?)

Converts a ComarkTree back into a markdown string, preserving frontmatter, component syntax, and attributes.

Parameters:

  • tree - The ComarkTree returned by parse() or createParse()
  • options? - Optional render options

Returns: Promise<string> — the serialized markdown string

Example:

import { parse } from 'comark'
import { renderMarkdown } from 'comark/render'

const tree = await parse(`---
title: Hello
---

# Hello **World**

::alert{type="info"}
This is an alert
::
`)

const markdown = await renderMarkdown(tree)
For full type definitions, see ComarkNode in the Comark AST reference.

Options

OptionTypeDefaultDescription
maxInlineAttributesnumber3Maximum attributes before switching to YAML block syntax. Set to 0 to always use block syntax
blockAttributesStyle'frontmatter' | 'codeblock''codeblock'Syntax style for block attributes when they exceed maxInlineAttributes
frontmatterOptionsDumpOptions{ indent: 2, lineWidth: -1 }js-yaml DumpOptions passed to the frontmatter serializer
componentsRecord<string, NodeHandler>{}Custom render handlers for specific elements
dataRecord<string, any>{}Additional data passed to render handlers

maxInlineAttributes

When a component has more attributes than maxInlineAttributes, Comark switches to YAML block syntax. The blockAttributesStyle option controls which block format is used:

// Renders as inline when 3 or fewer attributes
// ::card{title="Hello" icon="star" color="blue"}

// Switches to block syntax when more than 3

Switch between styles with the blockAttributesStyle option:

// Use codeblock style (default)
const md = await renderMarkdown(tree, { blockAttributesStyle: 'codeblock' })

// Use frontmatter style
const md = await renderMarkdown(tree, { blockAttributesStyle: 'frontmatter' })

// Always use block syntax with frontmatter style
const md = await renderMarkdown(tree, {
  maxInlineAttributes: 0,
  blockAttributesStyle: 'frontmatter',
})

frontmatterOptions

By default frontmatter strings are never line-wrapped (lineWidth: -1). Pass any js-yaml DumpOptions via frontmatterOptions to override this or control other serialization behaviour:

const md = await renderMarkdown(tree, {
  frontmatterOptions: { lineWidth: 80, sortKeys: true },
})

components

Override how specific elements are serialized back to markdown. Each handler receives the node and a context object with a render helper for recursing into children.

import { parse } from 'comark'
import { renderMarkdown } from 'comark/render'

const tree = await parse(source)

const markdown = await renderMarkdown(tree, {
  components: {
    alert: ([, attrs, ...children], { render }) => {
      return `::alert{type="${attrs.type}"}\n${render(children)}\n::`
    }
  }
})

data

Additional data passed to every component handler via the context object. Useful for sharing configuration or state across handlers:

const markdown = await renderMarkdown(tree, {
  data: { baseUrl: 'https://example.com' },
  components: {
    a: ([, attrs, ...children], { render, data }) => {
      const href = attrs.href?.startsWith('/')
        ? `${data.baseUrl}${attrs.href}`
        : attrs.href
      return `[${render(children)}](${href})`
    }
  }
})

Use Cases

Round-trip Transformation

Parse markdown, programmatically transform the AST, then serialize back:

import { parse } from 'comark'
import { renderMarkdown } from 'comark/render'
import { visit } from 'comark/utils'

const tree = await parse(source)

// Add an "external" class to all links
visit(tree,
  (node) => Array.isArray(node) && node[0] === 'a',
  (node) => {
    const el = node as [string, Record<string, any>, ...any[]]
    if (el[1].href?.startsWith('http')) {
      el[1].target = '_blank'
    }
  }
)

const output = await renderMarkdown(tree)

Content Migration

Convert between attribute styles or normalize formatting:

import { parse } from 'comark'
import { renderMarkdown } from 'comark/render'

async function normalizeDocument(source: string) {
  const tree = await parse(source)
  // Re-serialize with consistent formatting using codeblock style
  return renderMarkdown(tree, { maxInlineAttributes: 0 })
}

// Or migrate to frontmatter style
async function migrateToFrontmatter(source: string) {
  const tree = await parse(source)
  return renderMarkdown(tree, {
    maxInlineAttributes: 0,
    blockAttributesStyle: 'frontmatter',
  })
}

Programmatic Document Generation

Build a Comark document from data:

import { renderMarkdown } from 'comark/render'
import type { ComarkTree } from 'comark'

const tree: ComarkTree = {
  frontmatter: { title: 'API Reference', date: '2025-01-01' },
  meta: {},
  nodes: [
    ['h1', {}, 'API Reference'],
    ['p', {}, 'Welcome to the API docs.'],
    ['alert', { type: 'info' }, 'This API is in beta.'],
  ],
}

const markdown = await renderMarkdown(tree)