Warning
Comark 0.6.0 is a breaking release.
Components and functions are renamed to more generic, friendly names. Framework components are now Markdown / MarkdownDocument, parser entry points are parseMarkdown / createMarkdownParser, AST types use the MarkdownDocument / Node names, and props are unified on value (with documentKey for live documents). Old names fail at compile time or runtime — there are no compatibility shims left.
A full changelog for 0.6.0 is at the bottom of this page.
Using the prompt below, you can migrate your Comark usage to the new API. It covers every rename and gives a simple path through the breaking changes.
If you don't have an AI subscription:
- Use the Vercel AI Gateway free $5 monthly credit and wire it into your editor as the model provider
- Try OpenCode — they have a generous free tier that is more than enough for this migration and other tasks
⬇️ 📋 CLICK TO EXPAND THE FULL MIGRATION PROMPT 📋 ⬇️
You are migrating **this project** (a consumer of Comark) to **Comark 0.6.0+**.
In that release, Comark renamed components and functions to more generic, friendly names (including `@comark/html` and `@comark/ansi` APIs), and renamed the live-document key prop. Old component names, parser names, AST type aliases, HTML/ANSI helpers, and props no longer exist. Compatibility shims are gone — old code fails at compile time or runtime.
Your job:
1. Find every use of removed or renamed Comark APIs in **this repository only**.
2. Rewrite them to the current public API.
3. Do not reintroduce aliases or wrappers for old names.
4. Keep app behavior the same. This is a rename migration, not a feature rewrite.
5. Run this project’s typecheck/tests after edits and fix failures caused by the migration.
### Scope
- In scope: app/source code, components, pages, tests, Storybook, local docs/examples, re-export barrels, Nuxt auto-import usage.
- Out of scope: changing Comark package internals, forking Comark, or keeping dual `value ?? tree` / `value ?? markdown` / `documentKey ?? comarkKey` fallbacks.
### Important: what NOT to rename
These are still valid current APIs and must stay:
- Package names: `comark`, `@comark/vue`, `@comark/react`, `@comark/svelte`, `@comark/angular`, `@comark/nuxt`, `@comark/html`, `@comark/ansi`
- Plugin brand APIs: `ComarkPlugin`, `defineComarkPlugin`, `ComarkParseFn`, etc.
- Live context APIs: `createComarkContext`, `ComarkContext`, `globalThis.comarkContext`
- CSS hooks: `comark-content`, `comark-stream`
- Markdown syntax: `::component`, attributes, slots
- Local variables named `tree`/`markdown` are fine if they are not the removed **public prop/export** names
---
## Prerequisite
Confirm dependencies are on **Comark 0.6.0+** first. If the project is still on an older version, old names may still typecheck and the migration is incomplete.
Upgrade typically looks like:
```bash
# adjust to the package manager / packages this project uses
pnpm up comark @comark/vue @comark/react @comark/svelte @comark/angular @comark/nuxt @comark/html @comark/ansi
# or npm/yarn equivalents
```
Only migrate packages this project actually depends on.
---
## Complete breaking-change map
### 1) Parser functions & option types (`comark`)
| Removed | Replacement |
|---|---|
| `parse` | `parseMarkdown` |
| `createParse` | `createMarkdownParser` |
| `createSerializedParse` | `createSerializedMarkdownParser` |
| `ParseOptions` | `ParserOptions` |
| `RenderOptions` | `RendererOptions` |
```ts
// Before
import { parse, createParse, createSerializedParse } from 'comark'
import type { ParseOptions, RenderOptions } from 'comark'
const tree = await parse(md)
const parseFn = createParse({ autoClose: true })
const serialized = createSerializedParse()
// After
import { parseMarkdown, createMarkdownParser, createSerializedMarkdownParser } from 'comark'
import type { ParserOptions, RendererOptions } from 'comark'
const document = await parseMarkdown(md)
const parseFn = createMarkdownParser({ autoClose: true })
const serialized = createSerializedMarkdownParser()
```
Note: only replace `parse` when it is imported from `comark` / a Comark package. Do not rename unrelated local functions named `parse`.
### 2) AST / document types (`comark`)
| Removed type | Replacement |
|---|---|
| `ComarkTree` | `MarkdownDocument` |
| `ComarkElement` | `ElementNode` |
| `ComarkElementAttributes` | `ElementNodeAttributes` |
| `ComarkText` | `TextNode` |
| `ComarkComment` | `CommentNode` |
| `ComarkNode` | `Node` |
```ts
// Before
import type { ComarkTree, ComarkElement, ComarkText, ComarkNode } from 'comark'
// After
import type { MarkdownDocument, ElementNode, TextNode, Node } from 'comark'
```
### 3) HTML renderer (`@comark/html`)
| Removed | Replacement |
|---|---|
| `createRender` | `createHtmlRenderer` |
| `render` | `renderHtml` |
| `renderHTML` | `renderHtmlFromDocument` |
```ts
// Before
import { createRender, render, renderHTML } from '@comark/html'
import { parse } from 'comark'
import highlight from 'comark/plugins/highlight'
const renderMd = createRender({
plugins: [highlight()],
components: {
alert: ([, attrs, ...children], { render }) =>
`<div class="alert alert-${attrs.type}">${render(children)}</div>`,
},
})
const html = await renderMd('# Hello')
// or one-shot:
const html2 = await render('# Hello')
// or from a parsed tree:
const tree = await parse(md)
const html3 = await renderHTML(tree)
// After
import { createHtmlRenderer, renderHtml, renderHtmlFromDocument } from '@comark/html'
import { parseMarkdown } from 'comark'
import highlight from '@comark/html/plugins/highlight'
const renderMd = createHtmlRenderer({
plugins: [highlight()],
components: {
alert: async ([, attrs, ...children], { render }) =>
`<div class="alert alert-${attrs.type}">${await render(children)}</div>`,
},
})
const html = await renderMd('# Hello')
// or one-shot:
const html2 = await renderHtml('# Hello')
// or from a parsed document:
const document = await parseMarkdown(md)
const html3 = await renderHtmlFromDocument(document)
```
Notes:
- Prefer framework package plugin paths when available (`@comark/html/plugins/*`).
- Custom HTML component handlers may now be `async` and should `await render(children)`.
- Only rename `render` / `createRender` when imported from `@comark/html` (ANSI has its own renames below).
### 4) ANSI renderer (`@comark/ansi`)
| Removed | Replacement |
|---|---|
| `createLog` | `createAnsiWriter` |
| `log` | `writeAnsi` |
| `createRender` | `createAnsiRenderer` |
| `render` | `renderAnsi` |
| `renderANSI` | `renderAnsiFromDocument` |
| `LogOptions` | `AnsiWriterOptions` |
| `RenderANSIOptions` | `AnsiRendererOptions` |
| `write` (option key) | `writer` |
```ts
// Before
import { createLog, log, createRender, render, renderANSI } from '@comark/ansi'
import type { LogOptions, RenderANSIOptions } from '@comark/ansi'
import { parse } from 'comark'
import math from 'comark/plugins/math'
const print = createLog({
plugins: [math()],
width: 120,
write: (s) => process.stderr.write(s),
})
await print('# Hello')
await log('# Hello')
const toAnsi = createRender({ plugins: [math()] })
const out = await toAnsi('# Hello')
const out2 = await render('# Hello')
const tree = await parse(md)
const out3 = await renderANSI(tree)
// After
import {
createAnsiWriter,
writeAnsi,
createAnsiRenderer,
renderAnsi,
renderAnsiFromDocument,
} from '@comark/ansi'
import type { AnsiWriterOptions, AnsiRendererOptions } from '@comark/ansi'
import { parseMarkdown } from 'comark'
import math, { Math } from '@comark/ansi/plugins/math'
const print = createAnsiWriter({
plugins: [math()],
components: { Math },
width: 120,
writer: (s) => process.stderr.write(s),
})
await print('# Hello')
await writeAnsi('# Hello')
const toAnsi = createAnsiRenderer({ plugins: [math()], components: { Math } })
const out = await toAnsi('# Hello')
const out2 = await renderAnsi('# Hello')
const document = await parseMarkdown(md)
const out3 = await renderAnsiFromDocument(document)
```
Notes:
- `write` → `writer` only on ANSI writer options.
- Prefer `@comark/ansi/plugins/*` when using the ANSI package.
- Only rename `render` / `createRender` when imported from `@comark/ansi`.
### 5) Framework components
| Removed | Replacement | Where |
|---|---|---|
| `Comark` | `Markdown` | `@comark/vue`, `@comark/react`, `@comark/svelte`, Nuxt auto-import |
| `ComarkRenderer` | `MarkdownDocument` | `@comark/vue`, `@comark/react`, `@comark/svelte`, Nuxt auto-import |
| `MarkdownParsed` | `MarkdownDocument` | vue/react/svelte/angular |
| `ComarkClient` | `MarkdownClient` | `@comark/react` |
| `ComarkLive` | `MarkdownLive` | `@comark/react` |
| `ComarkAsync` | `MarkdownAsync` | `@comark/svelte/async` |
| `ComarkNode` (component) | `MarkdownNode` | `@comark/svelte`, `@comark/angular` |
| `ComarkComponent` | `Markdown` | `@comark/angular` |
| `ComarkRendererComponent` | `MarkdownDocument` | `@comark/angular` |
| `ComarkNodeComponent` / `NodeComponent` (Angular Comark node) | `MarkdownNode` | `@comark/angular` |
### 6) Define helpers and prop types
| Removed | Replacement |
|---|---|
| `defineComarkComponent` | `defineMarkdownComponent` |
| `defineComarkRendererComponent` | `defineMarkdownDocumentComponent` |
| `defineMarkdownParsedComponent` | `defineMarkdownDocumentComponent` |
| `DefineComarkComponentOptions` | `DefineMarkdownComponentOptions` |
| `DefineComarkRendererOptions` | `DefineMarkdownDocumentOptions` |
| `DefineMarkdownParsedOptions` | `DefineMarkdownDocumentOptions` |
| `ComarkProps` | `MarkdownProps` |
| `ComarkRendererProps` | `MarkdownDocumentProps` |
| `MarkdownParsedProps` | `MarkdownDocumentProps` |
| `ComarkNodeProps` | `MarkdownNodeProps` |
| `ComarkLiveProps` | `MarkdownLiveProps` |
### 7) Component props
| Removed / old prop | Replacement | Used on |
|---|---|---|
| `markdown` | `value` | `Markdown`, `MarkdownClient`, `MarkdownAsync`, defined markdown components |
| `tree` | `value` | `MarkdownDocument`, `MarkdownLive`, defined document components |
| `comarkKey` / `comark-key` | `documentKey` / `document-key` | live document subscription on `MarkdownDocument`, `MarkdownLive`, etc. |
`value` accepts:
- markdown **string** on high-level markdown components
- parsed **`MarkdownDocument`** on document/renderer components
- string or `MarkdownDocument` on high-level `Markdown`
```tsx
// Before
<Comark markdown={content} />
<ComarkRenderer tree={document} />
<Markdown markdown={content} />
<MarkdownDocument tree={document} />
<MarkdownLive value={document} comarkKey="page" />
// After
<Markdown value={content} />
<MarkdownDocument value={document} />
<MarkdownLive value={document} documentKey="page" />
```
```vue
<!-- Before -->
<Comark :markdown="content" />
<ComarkRenderer :tree="document" />
<MarkdownDocument comark-key="page" :value="document" />
<!-- After -->
<Markdown :value="content" />
<MarkdownDocument :value="document" />
<MarkdownDocument document-key="page" :value="document" />
```
```svelte
<!-- Before -->
<Comark markdown={content} />
<ComarkRenderer tree={document} />
<MarkdownDocument comarkKey="page" value={document} />
<!-- After -->
<Markdown value={content} />
<MarkdownDocument value={document} />
<MarkdownDocument documentKey="page" value={document} />
```
```html
<!-- Angular before -->
<comark-markdown-document comarkKey="page" [value]="document"></comark-markdown-document>
<!-- Angular after -->
<comark-markdown-document documentKey="page" [value]="document"></comark-markdown-document>
```
### 8) Angular selectors
| Removed selector | Replacement |
|---|---|
| `<comark>` | `<comark-markdown>` |
| `<comark-renderer>` | `<comark-markdown-document>` |
| `<comark-markdown-parsed>` | `<comark-markdown-document>` |
| `<comark-node>` | `<comark-markdown-node>` |
```html
<!-- Before -->
<comark [markdown]="content" [components]="components"></comark>
<comark-renderer [tree]="document"></comark-renderer>
<comark-markdown-parsed [value]="document"></comark-markdown-parsed>
<!-- After -->
<comark-markdown [value]="content" [components]="components"></comark-markdown>
<comark-markdown-document [value]="document"></comark-markdown-document>
```
### 9) Nuxt auto-imports
If this project uses `@comark/nuxt`, these auto-imports are gone:
- components: `Comark`, `ComarkRenderer`
- helpers: `defineComarkComponent`, `defineComarkRendererComponent`
Use:
- components: `Markdown`, `MarkdownDocument`
- helpers: `defineMarkdownComponent`, `defineMarkdownDocumentComponent`
Search templates even when there is no explicit import.
### 10) Deep imports of deleted files
Rewrite any deep imports of removed component files, for example:
- `.../Comark`, `.../ComarkRenderer`
- `.../ComarkClient`, `.../ComarkLive`
- `.../ComarkAsync`, `.../ComarkNode`
- any `internal/deprecation` helper from Comark packages
Prefer package root imports:
```ts
import { Markdown, MarkdownDocument, defineMarkdownComponent } from '@comark/vue'
import { Markdown, MarkdownDocument, MarkdownClient, MarkdownLive } from '@comark/react'
import { Markdown, MarkdownDocument } from '@comark/svelte'
import { MarkdownAsync } from '@comark/svelte/async'
import { Markdown, MarkdownDocument, MarkdownNode } from '@comark/angular'
import { parseMarkdown, type MarkdownDocument } from 'comark'
```
---
## Search checklist
Run these searches across the consumer project:
```text
\bComark\b
ComarkRenderer
ComarkClient
ComarkLive
ComarkAsync
ComarkNode
ComarkComponent
ComarkTree
ComarkElement
ComarkText
ComarkComment
ComarkElementAttributes
MarkdownParsed
defineComarkComponent
defineComarkRendererComponent
defineMarkdownParsedComponent
createParse\b
createSerializedParse\b
ParseOptions
RenderOptions
from ['"]comark['"]
from ['"]@comark/html['"]
from ['"]@comark/ansi['"]
createRender\b
renderHTML
renderHtmlFromDocument
createLog\b
\blog\b
renderANSI
LogOptions
RenderANSIOptions
write:
markdown=
:markdown=
markdown={
\btree=
:tree=
tree={
comarkKey
comark-key
comark-renderer
comark-markdown-parsed
comark-node
<comark>
```
Also inspect:
- `.vue` / `.tsx` / `.jsx` / `.svelte` / Angular templates
- server / CLI code using `@comark/html` or `@comark/ansi`
- tests and snapshots
- Storybook stories
- README / internal docs in this project
- Nuxt pages relying on auto-imports
- type-only imports and re-export barrels
When reviewing `\bComark\b` hits, keep package/plugin/context brand names listed in “what NOT to rename”.
When reviewing `createRender` / `render` / `log` / `write`, only rewrite imports from `@comark/html` or `@comark/ansi`.
---
## Migration procedure
1. **Upgrade deps** to **Comark 0.6.0+**.
2. **Inventory** all search hits and group by package/framework (`comark`, html, ansi, vue/react/svelte/angular/nuxt).
3. **Rewrite imports/symbols** to replacements.
4. **Rewrite components/selectors** in templates.
5. **Rewrite HTML/ANSI helpers**
- `@comark/html`: `createRender` → `createHtmlRenderer`, `render` → `renderHtml`, `renderHTML` → `renderHtmlFromDocument`
- `@comark/ansi`: `createLog`/`log` → `createAnsiWriter`/`writeAnsi`, `createRender`/`render` → `createAnsiRenderer`/`renderAnsi`, `renderANSI` → `renderAnsiFromDocument`, `write` option → `writer`
6. **Rewrite props**
- `markdown` → `value`
- `tree` → `value`
- `comarkKey` / `comark-key` → `documentKey` / `document-key`
- remove dual-prop fallbacks
7. **Rewrite types** (`ComarkTree` → `MarkdownDocument`, `ParseOptions` → `ParserOptions`, `RenderOptions` → `RendererOptions`, etc.).
8. **Update this project’s tests/docs/stories**.
9. **Validate with this project’s tooling**
- typecheck
- unit/integration tests
- smoke one real page/route (and CLI path if using ANSI) that renders markdown
10. **Done when**
- no removed symbols/props/selectors remain
- typecheck/tests pass
- no compatibility wrappers were added back
---
## Framework examples
### Vue
```vue
<!-- Before -->
<script setup lang="ts">
import { Comark, ComarkRenderer, defineComarkComponent } from '@comark/vue'
import type { ComarkTree } from 'comark'
import { parse } from 'comark'
const tree = await parse(content) as ComarkTree
const Docs = defineComarkComponent({ name: 'Docs' })
</script>
<template>
<Comark :markdown="content" />
<ComarkRenderer :tree="tree" />
<MarkdownDocument comark-key="page" :value="tree" />
<Docs :markdown="content" />
</template>
```
```vue
<!-- After -->
<script setup lang="ts">
import { Markdown, MarkdownDocument, defineMarkdownComponent } from '@comark/vue'
import type { MarkdownDocument as Document } from 'comark'
import { parseMarkdown } from 'comark'
const document = await parseMarkdown(content) as Document
const Docs = defineMarkdownComponent({ name: 'Docs' })
</script>
<template>
<Markdown :value="content" />
<MarkdownDocument :value="document" />
<MarkdownDocument document-key="page" :value="document" />
<Docs :value="content" />
</template>
```
### React / Next.js
```tsx
// Before
import { Comark, ComarkRenderer, ComarkClient, ComarkLive, defineComarkComponent } from '@comark/react'
import { parse } from 'comark'
const document = await parse(md)
export const Docs = defineComarkComponent({ name: 'Docs' })
return (
<>
<Comark markdown={md} />
<ComarkRenderer tree={document} />
<ComarkClient markdown={md} />
<ComarkLive tree={document} comarkKey="doc" />
</>
)
```
```tsx
// After
import { Markdown, MarkdownDocument, MarkdownClient, MarkdownLive, defineMarkdownComponent } from '@comark/react'
import { parseMarkdown } from 'comark'
const document = await parseMarkdown(md)
export const Docs = defineMarkdownComponent({ name: 'Docs' })
return (
<>
<Markdown value={md} />
<MarkdownDocument value={document} />
<MarkdownClient value={md} />
<MarkdownLive value={document} documentKey="doc" />
</>
)
```
### Svelte / SvelteKit
```svelte
<!-- Before -->
<script>
import { Comark, ComarkRenderer } from '@comark/svelte'
import { ComarkAsync } from '@comark/svelte/async'
</script>
<Comark markdown={content} />
<ComarkRenderer tree={document} />
<MarkdownDocument comarkKey="page" value={document} />
<ComarkAsync markdown={content} />
```
```svelte
<!-- After -->
<script>
import { Markdown, MarkdownDocument } from '@comark/svelte'
import { MarkdownAsync } from '@comark/svelte/async'
</script>
<Markdown value={content} />
<MarkdownDocument value={document} />
<MarkdownDocument documentKey="page" value={document} />
<MarkdownAsync value={content} />
```
### Angular
```ts
// Before
import { ComarkComponent, ComarkRendererComponent, defineComarkComponent } from '@comark/angular'
```
```html
<!-- Before -->
<comark [markdown]="content"></comark>
<comark-renderer [tree]="document"></comark-renderer>
<comark-markdown-parsed comarkKey="page" [value]="document"></comark-markdown-parsed>
```
```ts
// After
import { Markdown, MarkdownDocument, defineMarkdownComponent } from '@comark/angular'
```
```html
<!-- After -->
<comark-markdown [value]="content"></comark-markdown>
<comark-markdown-document documentKey="page" [value]="document"></comark-markdown-document>
```
### Nuxt
```vue
<!-- Before: often auto-imported, no import statement -->
<template>
<Comark :markdown="content" />
<ComarkRenderer :tree="document" />
</template>
```
```vue
<!-- After -->
<template>
<Markdown :value="content" />
<MarkdownDocument :value="document" />
</template>
```
### Core parser-only usage
```ts
// Before
import { parse, createParse, type ComarkTree } from 'comark'
const parseMarkdownLegacy = createParse()
const tree: ComarkTree = await parse(source)
// After
import { parseMarkdown, createMarkdownParser, type MarkdownDocument } from 'comark'
const parse = createMarkdownParser()
const document: MarkdownDocument = await parseMarkdown(source)
// or: const document = await parse(source)
```
### HTML (`@comark/html`)
```ts
// Before
import { createRender, render, renderHTML } from '@comark/html'
import { parse } from 'comark'
const renderMd = createRender({ plugins: [] })
const html = await render('# Hello')
const tree = await parse(md)
const html2 = await renderHTML(tree)
// After
import { createHtmlRenderer, renderHtml, renderHtmlFromDocument } from '@comark/html'
import { parseMarkdown } from 'comark'
const renderMd = createHtmlRenderer({ plugins: [] })
const html = await renderHtml('# Hello')
const document = await parseMarkdown(md)
const html2 = await renderHtmlFromDocument(document)
```
### ANSI (`@comark/ansi`)
```ts
// Before
import { createLog, log, createRender, render, renderANSI } from '@comark/ansi'
import { parse } from 'comark'
const print = createLog({ write: (s) => process.stderr.write(s) })
await log('# Hello')
const out = await render('# Hello')
const tree = await parse(md)
const out2 = await renderANSI(tree)
// After
import {
createAnsiWriter,
writeAnsi,
createAnsiRenderer,
renderAnsi,
renderAnsiFromDocument,
} from '@comark/ansi'
import { parseMarkdown } from 'comark'
const print = createAnsiWriter({ writer: (s) => process.stderr.write(s) })
await writeAnsi('# Hello')
const out = await renderAnsi('# Hello')
const document = await parseMarkdown(md)
const out2 = await renderAnsiFromDocument(document)
```
---
## Suggested commit message for the consumer project
```text
refactor: migrate Comark usage to Markdown APIs after rename
```
---
## Final verification in the consumer project
```bash
# leftover search (adjust tool if needed)
rg -n "ComarkRenderer|ComarkClient|ComarkLive|ComarkAsync|MarkdownParsed|defineComarkComponent|defineComarkRendererComponent|ComarkTree|createParse|createSerializedParse|ParseOptions|RenderOptions|renderHTML|renderANSI|createLog|LogOptions|RenderANSIOptions|comarkKey|comark-key|comark-markdown-parsed|comark-renderer" .
# also check HTML/ANSI package imports if used
rg -n "from ['\"]@comark/(html|ansi)['\"]" -n .
# then this project's checks
pnpm typecheck
pnpm test
# or npm/yarn/ vitest/ tsc equivalents used here
```
If removed symbols still typecheck, the project is probably still resolving an older Comark version. Fix dependency versions, reinstall, and rerun the migration.Changelog
⚠️ Breaking Changes
- Rename the public API to more generic, friendly Markdown-first names (
Markdown/MarkdownDocument,parseMarkdown,value,documentKey, AST types) - by @atinux and @farnabaz in #318 (fc940)
🚀 Features
- Introduce
headingIdsoption to disable auto-generated ids for headings - by @farnabaz in #284 (b2988) - Introduce
unwrapfeature in parser - by @farnabaz in #275 (060df)
🐞 Bug Fixes
- Handle bracketed spans inside link labels - by @deshiknaves and @farnabaz in #288 (baff1)
- Preserve image attributes when a title is present - by @nimonian, @farnabaz and @atinux in #290 (8d217)
- Do not include standard html tag names in heading slugs - by @danielroe in #309 (0cd5f)
- Ignore props with invalid chars - by @farnabaz in #298 (849c7)
- Sanitize Vue directive attributes - by @farnabaz in #285 (5a823)
- auto-close:
- breaks:
- comark:
- Only complete unclosed frontmatter while streaming - by @sandros94 and @farnabaz in #269 (aa8c2)
- Consume nested brackets in inline component content - by @sandros94 in #306 (56e39)
- highlight:
- parse:
- Keep
attr="true|false"as string - by @arashsheyda in #321 (3de08)
- Keep
- stringify:
- Render children of inline components in inline form - by @sandros94 in #305 (7d9a5)
- syntax:
- Stop 2+ blank lines from closing nested components early - by @adamdehaven and @farnabaz in #323 (83f67)
- yaml:
- Resolve empty props blocks instead of throwing - by @adamdehaven and @farnabaz in #324 (71fbe)