mit einem Klick
migrate-mdc-to-comark
// Migrate a Nuxt project from @nuxtjs/mdc to Comark. Covers package changes, parse/render API mapping, AST format, Nuxt module config, components, slots, plugins, and Nuxt UI integration.
// Migrate a Nuxt project from @nuxtjs/mdc to Comark. Covers package changes, parse/render API mapping, AST format, Nuxt module config, components, slots, plugins, and Nuxt UI integration.
Comark (Components in Markdown) parser โ syntax, AST, Vue/React/Svelte renderers, plugins, and LLM streaming with auto-close.
Comark (Components in Markdown) parser โ syntax, AST, Vue/React/Svelte renderers, plugins, and LLM streaming with auto-close.
| name | migrate-mdc-to-comark |
| description | Migrate a Nuxt project from @nuxtjs/mdc to Comark. Covers package changes, parse/render API mapping, AST format, Nuxt module config, components, slots, plugins, and Nuxt UI integration. |
@nuxtjs/mdc was Nuxt-only. Comark is its successor โ the markdown syntax is fully compatible, your .md files need no changes. What changes is the JavaScript API.
The migration has two parts: Core Package (programmatic API) and Nuxt Module (components, slots, config).
@nuxtjs/mdc โ comark (core only) or @comark/nuxt (Nuxt module)parseMarkdown() โ parse() ยท factory: createParse() (sync, no await)stringifyMarkdown() โ renderMarkdown() from comark/render['tag', props, ...children]result.body / result.data โ tree.nodes / tree.frontmatter<MDCRenderer :body :data> โ <ComarkRenderer :tree><MDC :value> โ <Comark :markdown><MDCSlot /> โ native <slot />nuxt.config โ per-component defineComarkComponent({ plugins })@nuxtjs/mdc | comark |
|---|---|
parseMarkdown(md, opts) | parse(md, opts) from comark |
createMarkdownParser(opts) (async) | createParse(opts) (sync โ no await) |
stringifyMarkdown(body, data) | renderMarkdown(tree) from comark/render |
result.body (MDCRoot) | tree.nodes (ComarkNode[]) |
result.data | tree.frontmatter |
result.data.title | tree.frontmatter.title |
result.toc | tree.meta.toc (requires toc plugin) |
result.excerpt | tree.meta.summary (requires summary plugin) |
@nuxtjs/mdc | comark |
|---|---|
{ type: 'root', children: MDCNode[] } | { nodes: ComarkNode[], frontmatter: {}, meta: {} } |
{ type: 'element', tag: 'p', props: {}, children: [] } | ['p', {}, ...children] |
{ type: 'text', value: 'hello' } | 'hello' (plain string) |
// Before โ MDCParseOptions
{
remark: { plugins: { /* record */ } },
rehype: { options: {...}, plugins: { /* record */ } },
highlight: { theme: '...', langs: [...] } | false,
toc: { depth: 3, searchDepth: 2 } | false,
}
// After โ ParseOptions
{
plugins: ComarkPlugin[], // ordered array, not a record
autoUnwrap: true, // removes <p> from single-paragraph containers
autoClose: true, // completes incomplete syntax (useful for streaming)
html: true, // parse embedded HTML tags
}
The unified/remark/rehype pipeline is replaced by Comark's own lighter plugin interface.
| Feature | Before | After |
|---|---|---|
| Syntax highlighting | rehypeHighlight via createMarkdownParser | highlight() from comark/plugins/highlight |
| Table of Contents | parseMarkdown(md, { toc: { depth: 3 } }) | toc({ depth: 3 }) plugin |
| Excerpt / Summary | result.excerpt (built-in) | summary() plugin โ tree.meta.summary |
| Emoji | remark-emoji (enabled by default) | emoji() plugin (opt-in) |
Available plugins: comark/plugins/toc, comark/plugins/highlight, comark/plugins/emoji, comark/plugins/task-list, comark/plugins/summary, comark/plugins/security, comark/plugins/alert, comark/plugins/math, comark/plugins/mermaid, comark/plugins/punctuation
// Before
export default defineNuxtConfig({
modules: ['@nuxtjs/mdc'],
mdc: { highlight: { ... }, remarkPlugins: { ... } },
})
// After
export default defineNuxtConfig({
modules: ['@comark/nuxt'],
// No plugin config here โ plugins are defined per-component
})
@comark/nuxt auto-imports: Comark, ComarkRenderer, defineComarkComponent, defineComarkRendererComponent.
@nuxtjs/mdc | @comark/nuxt |
|---|---|
<MDCRenderer :body :data :components> | <ComarkRenderer :tree :components> |
<MDC :value :parser-options> | <Comark :markdown :options> or <Comark>{{ md }}</Comark> |
<MDCSlot /> | <slot /> |
<MDCSlot unwrap="p" /> | <slot unwrap="p" /> |
<slot mdc-unwrap="p" /> | <slot unwrap="p" /> |
For a pre-parsed tree, use <ComarkRenderer> directly instead of <Comark>.
<ComarkRenderer> props changesMDC <MDCRenderer> | Comark <ComarkRenderer> | Notes |
|---|---|---|
body (MDCRoot) | tree (ComarkTree) | Different AST shape |
data | โ | Frontmatter is in tree.frontmatter |
tag | โ | Wrapper is always <div class="comark-content"> |
prose | โ | Prose* resolution is automatic |
unwrap | โ | Use autoUnwrap in parse options |
components | components | Same purpose |
| โ | componentsManifest | New: dynamic async component resolver |
| โ | streaming | New: streaming mode |
| โ | caret | New: animated caret for streaming |
<!-- Before -->
<MDCRenderer :body="result.excerpt ?? result.body" :data="result.data" />
<!-- After -->
<Comark summary>{{ markdown }}</Comark>
defineComarkComponentReplaces global mdc: { ... } config. Define reusable components with their own plugins and component mappings:
import { defineComarkComponent } from '@comark/vue'
import highlight from 'comark/plugins/highlight'
import toc from 'comark/plugins/toc'
export const ArticleComark = defineComarkComponent({
name: 'ArticleComark',
plugins: [highlight({ themes: { light: githubLight, dark: githubDark } }), toc()],
components: { alert: CustomAlert },
})
<MDCSlot /> โ native <slot />. Named slots work the same (#slotName in markdown, <slot name="slotName"> in component). The unwrap attribute (<slot unwrap="p">) strips wrapper tags from children.
Same Prose*.vue naming convention in components/prose/. Resolution changed from kebab-case (prose-p) to PascalCase (ProseP) internally โ no file changes needed.
When using Nuxt UI, @comark/nuxt registers Nuxt UI prose components automatically. Shorthand callout components are available:
::note โ informational
::tip โ helpful suggestion
::warning โ something to watch out for
::caution โ critical warning
These are only available with Nuxt UI. Without it, use ::callout{icon="..." color="..."}.
createParse is sync โ no await needed (unlike createMarkdownParser)attrs.lang, not attrs.languagetree.frontmatter, not passed separatelyrenderMarkdown includes frontmatter โ reads tree.frontmatter automaticallyunified pipeline โ mdc.config.ts hooks (pre, remark, rehype, post) have no equivalent, use ComarkPlugin interface insteadremark-emojiThe MDC block and inline component syntax is identical โ no changes needed in .md files.
{{ variable }}) โ not supported, rendered as plain textparseMarkdown(md, { data: { ... } })