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