Migrating from MDX
If you're coming from MDX, this guide maps every MDX concept to its Comark equivalent. The core idea is the same, embed components in Markdown — but Comark uses a text-friendly syntax instead of JSX.
Quick Comparison
| Concept | MDX | Comark |
|---|---|---|
| Block component | <Alert>...</Alert> | ::alert ... :: |
| Inline component | <Badge>text</Badge> | :badge[text] |
| Props | <Alert type="info"> | ::alert{type="info"} |
| Self-closing | <Divider /> | ::divider\n:: |
| Dynamic binding | year={year} | :year="year" |
| Children | JSX children | Markdown between :: delimiters |
| Imports | import X from './X' | Component registered in renderer |
| Exports | export const x = 1 | Frontmatter or application state |
Components
Block Components
MDX uses JSX tags. Comark uses :: fences:
<Alert type="info">
This is an important message with **bold** text.
</Alert>::alert{type="info"}
This is an important message with **bold** text.
::Inline Components
Status: <Badge color="green">Active</Badge>Status: :badge[Active]{color="green"}Self-Closing Components
<Divider />
<Chart color="#fcb32c" />::divider
::
::chart{color="#fcb32c"}
::Nested Components
<Card title="Example">
<List>
- Item 1
- Item 2
</List>
</Card>::card{title="Example"}
:::list
- Item 1
- Item 2
:::
::Nesting in Comark uses increasing colons: :: for outer, ::: for inner, :::: for deeper levels.
Props & Attributes
Static Props
<Alert type="info" dismissible>
Content
</Alert>::alert{type="info" dismissible}
Content
::Dynamic / Expression Binding
MDX uses JSX curly-brace expressions. Comark uses :prop="value" binding syntax:
<Chart year={currentYear} data={chartData} />::chart{:year="currentYear" :data="chartData"}
::The : prefix marks a prop as a dynamic binding — the value is treated as an expression rather than a literal string.
Numeric & Boolean Props
<Grid columns={3} fluid={true} />::grid{:columns="3" fluid}
::Boolean props without a value default to true in both formats.
Imports & Component Registration
MDX imports components per-file. In Comark, components are registered once in the renderer:
import Alert from './components/Alert'
import Chart from './components/Chart'
# Dashboard
<Alert type="info">Welcome!</Alert>
<Chart data={metrics} /><script setup>
import { Comark } from '@comark/vue'
import Alert from './components/Alert.vue'
import Chart from './components/Chart.vue'
const markdown = `
# Dashboard
::alert{type="info"}
Welcome!
::
::chart{:data="metrics"}
::
`
</script>
<template>
<Suspense>
<Comark :components="{ Alert, Chart }">{{ markdown }}</Comark>
</Suspense>
</template>import { Comark } from '@comark/react'
import { Alert } from './components/Alert'
import { Chart } from './components/Chart'
const markdown = `
# Dashboard
::alert{type="info"}
Welcome!
::
::chart{:data="metrics"}
::
`
export default function Page() {
return <Comark components={{ Alert, Chart }}>{markdown}</Comark>
}For shared defaults, use defineComarkComponent to avoid passing components everywhere:
import { defineComarkComponent } from '@comark/vue' // or '@comark/react'
import Alert from './components/Alert'
import Chart from './components/Chart'
export const DocsComark = defineComarkComponent({
name: 'DocsComark',
components: { Alert, Chart },
})Exports & State
MDX uses export statements to define page-level variables:
export const year = 2023
# Report for {year}Comark doesn't support inline JavaScript expressions. Instead, use frontmatter for static metadata and application state for dynamic values:
---
year: 2023
---
# Report for {{ year }}<script setup>
const year = ref(2023)
const markdown = computed(() => `# Report for ${year.value}`)
</script>
<template>
<Suspense>
<Comark>{{ markdown }}</Comark>
</Suspense>
</template>Expressions in Text
MDX allows JavaScript expressions anywhere in text:
The result is {2 + 3}.
Today is {new Date().toLocaleDateString()}.Comark does not evaluate inline expressions. Compute values in your application code and interpolate them into the markdown string before parsing:
const result = 2 + 3
const today = new Date().toLocaleDateString()
const markdown = `
The result is ${result}.
Today is ${today}.
`
const tree = await parse(markdown)Layouts
MDX supports layout components via default exports:
export { default } from './layouts/BlogPost'
# My PostIn Comark, wrap the renderer with your layout component:
<template>
<BlogPostLayout>
<Suspense>
<Comark>{{ content }}</Comark>
</Suspense>
</BlogPostLayout>
</template>Markdown Features
Standard markdown works identically in both formats. No changes needed for:
- Headings, paragraphs, lists
- Bold, italic, strikethrough
- Links and images
- Code blocks and inline code
- Blockquotes
- Tables
- Horizontal rules
Migration Checklist
- Replace JSX tags with Comark
::component/:componentsyntax - Move imports from per-file
importstatements to renderercomponentsoption - Convert expression props
prop={expr}to:prop="expr" - Replace inline expressions
{expr}with template string interpolation - Move exports to frontmatter or application state
- Remove layout exports and wrap with layout components instead
- Register plugins (math, mermaid, highlight) in your renderer setup