---
title: "Migrating from MDX"
description: "Map every MDX concept (components, props, expressions, imports and exports) to its Comark equivalent, with a step-by-step migration checklist."
canonical_url: "https://comark.dev/kb/migration-from-mdx"
---
# Migrating from MDX

> Map every MDX concept (components, props, expressions, imports and exports) to its Comark equivalent, with a step-by-step migration checklist.

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:

<code-group>
```js [MDX]
<Alert type="info">
  This is an important message with **bold** text.
</Alert>
```


```mdc [Comark]
::alert{type="info"}
This is an important message with **bold** text.
::
```
</code-group>

### Inline components

<code-group>
```js [MDX]
Status: <Badge color="green">Active</Badge>
```


```mdc [Comark]
Status: :badge[Active]{color="green"}
```
</code-group>

### Self-closing components

<code-group>
```js [MDX]
<Divider />
<Chart color="#fcb32c" />
```


```mdc [Comark]
‎::divider
::

‎::chart{color="#fcb32c"}
::
```
</code-group>

### Nested components

<code-group>
```js [MDX]
<Card title="Example">
  <List>
    - Item 1
    - Item 2
  </List>
</Card>
```


```mdc [Comark]
::card{title="Example"}
  :::list
  - Item 1
  - Item 2
  :::
::
```
</code-group>

Nesting in Comark uses increasing colons: `::` for outer, `:::` for inner, `::::` for deeper levels.

---

## Props & attributes

### Static props

<code-group>
```js [MDX]
<Alert type="info" dismissible>
  Content
</Alert>
```


```mdc [Comark]
::alert{type="info" dismissible}
Content
::
```
</code-group>

### Dynamic / expression binding

MDX uses JSX curly-brace expressions. Comark uses `:prop="value"` binding syntax:

<code-group>
```js [MDX]
<Chart year={currentYear} data={chartData} />
```


```mdc [Comark]
‎::chart{:year="currentYear" :data="chartData"}
::
```
</code-group>

The `:` prefix marks a prop as a dynamic binding: the value is treated as an expression rather than a literal string.

### Numeric & boolean props

<code-group>
```js [MDX]
<Grid columns={3} fluid={true} />
```


```mdc [Comark]
‎::grid{:columns="3" fluid}
::
```
</code-group>

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:

<code-group>
```js [MDX]
import Alert from './components/Alert'
import Chart from './components/Chart'

# Dashboard

<Alert type="info">Welcome!</Alert>

<Chart data={metrics} />
```


~~~vue [Comark (Vue)]
<script setup>
import { Markdown } 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>
    <Markdown :components="{ Alert, Chart }">{{ markdown }}</Markdown>
  </Suspense>
</template>
~~~


~~~tsx [Comark (React)]
import { Markdown } 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 <Markdown components={{ Alert, Chart }}>{markdown}</Markdown>
}
~~~
</code-group>

For shared defaults, use `defineMarkdownComponent` to avoid passing components everywhere:

```typescript
import { defineMarkdownComponent } from '@comark/vue' // or '@comark/react'
import Alert from './components/Alert'
import Chart from './components/Chart'

export const DocsMarkdown = defineMarkdownComponent({
  name: 'DocsMarkdown',
  components: { Alert, Chart },
})
```

---

## Exports & state

MDX uses `export` statements to define page-level variables:

```js
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:

<code-group>
```mdc [Frontmatter]
---
year: 2023
---

# Report for {{ year }}
```


```vue [Application State (Vue)]
<script setup>
const year = ref(2023)
const markdown = computed(() => `# Report for ${year.value}`)
</script>

<template>
  <Suspense>
    <Markdown>{{ markdown }}</Markdown>
  </Suspense>
</template>
```
</code-group>

---

## Expressions in text

MDX allows JavaScript expressions anywhere in text:

```js
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:

~~~typescript
const result = 2 + 3
const today = new Date().toLocaleDateString()

const markdown = `
The result is ${result}.
Today is ${today}.
`

const tree = await parseMarkdown(markdown)
~~~

---

## Layouts

MDX supports layout components via default exports:

```js
export { default } from './layouts/BlogPost'

# My Post
```

In Comark, wrap the renderer with your layout component:

```vue
<template>
  <BlogPostLayout>
    <Suspense>
      <Markdown>{{ content }}</Markdown>
    </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

1. **Replace JSX tags** with Comark `::component` / `:component` syntax
2. **Move imports** from per-file `import` statements to renderer `components` option
3. **Convert expression props** `prop={expr}` to `:prop="expr"`
4. **Replace inline expressions** `{expr}` with template string interpolation
5. **Move exports** to frontmatter or application state
6. **Remove layout exports** and wrap with layout components instead
7. **Register plugins** (math, mermaid, highlight) in your renderer setup

---

- [Component Syntax](https://comark.dev/syntax/components)
- [Attributes](https://comark.dev/syntax/attributes)


## Sitemap

See the full [sitemap](https://comark.dev/sitemap.md) for all pages.
