Comark: Parse and Render Markdown Anywhere
Markdown won the authoring war. It is readable, portable, widely supported, and understood by both humans and machines. The remaining challenge is everything that happens after authoring: parsing it reliably, transforming it as data, rendering it to different targets, and displaying it while content is still arriving.
Comark provides one JavaScript library for that complete Markdown pipeline.
Why Markdown?
Every piece of web content serves two audiences:
- Humans who need scannable prose
- Machines who need predictable structure
HTML is powerful but verbose. That verbosity costs even more in an era where AI agents pay per token. JSON is structured but no one wants to write a blog post in it.
Markdown occupies rare middle ground. The source text is comfortable prose for a human author and token-efficient structured data for a machine consumer. That convergence wasn't part of Gruber's original design. He was solving for readability in 2004. But it explains why Markdown has become the default format for both human authoring and AI consumption.
Why Machines Prefer Markdown
The evidence is clearest in token economics.
The same page served as HTML versus Markdown to an AI agent can use 80% fewer tokens in Markdown form. At scale, that is not a formatting preference, it is an infrastructure decision. Lower token counts mean faster responses, lower costs, and longer effective context windows.
Beyond token count, Markdown's explicit structure maps cleanly to how language models reason. A ## heading is unambiguously a section boundary. HTML carries the same information buried in <h2 class="..."> but the signal-to-noise ratio is far worse.
AI as a First-Class Audience
The tooling industry has converged on this conclusion. Streamdown (an open-source renderer by Hayden Bleasel, DX Engineer at Vercel) was built precisely because LLMs stream token by token and existing renderers like react-markdown assume a complete document, making them unable to handle unterminated blocks gracefully. Because LLMs tend to produce Markdown by default, Streamdown is a React component designed to render it correctly as chunks arrive, pairing naturally with useChat and other streaming patterns from @ai-sdk/react. Together they reflect a deliberate recognition that AI agents are now a primary consumer of content, not a secondary one (Comark vs Streamdown).
The same shift is visible in developer tooling. Files like AGENTS.md and .cursorrules are Markdown documents that configure AI coding assistants. GitHub Copilot reads them across sessions. Cursor injects them into every prompt. Claude Code uses CLAUDE.md. The format that humans write is the same format that machines act on.
This is new. For most of computing history, human-authored content and machine-readable configuration were different artifacts in different formats. Markdown is collapsing that boundary.
Why Comark?
The JavaScript ecosystem has excellent Markdown parsers and renderers, but most tools solve only one part of the pipeline. A renderer may be tied to one UI framework. A build-time compiler may not accept content from a database or live API. A conventional parser may assume the document is complete, which causes broken formatting during an AI stream.
Comark separates these concerns. It parses Markdown into a shared document model, lets you transform or store that document, and renders it through the output package you choose. The same API works at build time or runtime, on the server or in the browser, and with complete or streaming input.
Built on markdown-it Foundations
Comark supports CommonMark and GitHub Flavored Markdown (GFM) through markdown-exit, a TypeScript rewrite of markdown-it that preserves its plugin API. Existing Markdown stays familiar, and existing markdown-it plugins can work alongside Comark plugins.
This foundation gives Comark a mature parsing model without limiting how or where the parsed document is rendered.
Markdown as Data, Not Code
parseMarkdown(markdown) returns a compact, serializable MarkdownDocument. Text stays as strings, elements use tuples such as ['tag', props, ...children], and plugins can attach structured metadata.
That shared document boundary enables a complete content pipeline:
- A file, database, CMS, or API provides the raw Markdown
- On request, the server runs
parseMarkdown(markdown)and gets theMarkdownDocumentobject - Your application inspects, transforms, caches, or sends the document as JSON
- An HTML, ANSI, Vue, React, Svelte, or Angular renderer produces the final output
You can parse once and render many times without coupling stored content to a framework or build pipeline.
MarkdownDocument directly in the database to skip re-parsing on every request.One Source, Every Renderer
Most Markdown rendering libraries are designed around one output target. Comark decouples parsing from rendering so the same source can produce:
renderHtml()in @comark/html: server-side HTML strings, RSS, emails, and static buildsrenderAnsi()in @comark/ansi: styled terminal output for CLIs<Markdown>in Vue: async components, streaming, and slot-based composition<Markdown>in React: Server Components and client-side rendering<Markdown>in Svelte: snippets and streaming<Markdown>in Angular: standalone components with signals and streaming
Your content outlasts your output target and framework choice.
Streaming Is Part of the Parser
AI models emit Markdown token by token, but conventional parsers expect complete documents. Comark's auto-close support completes unterminated emphasis, links, code fences, and component blocks so each intermediate frame renders correctly.
Framework renderers expose streaming directly, while the core parser lets any integration opt into the same behavior. You can display content as soon as it arrives without maintaining a separate streaming syntax or renderer.
Extend Markdown Without Embedding Code
Standard Markdown does not express a warning callout, tabbed code group, video embed, or pricing card. Raw HTML can represent them, but it adds verbosity and couples content to markup. MDX allows executable JSX, but it also makes content part of a framework-specific build pipeline (full comparison).
Comark keeps extensions in readable plain text. Its component syntax is inspired by the Markdown directives proposal and builds on more than five years of production use in MDC:
::alert{type="warning"}
This action cannot be undone.
::A human can read the source, while the parser sees a component named alert with a type attribute and text content. The renderer only instantiates components registered by the application, so content remains data rather than executable code.
Attributes provide the same plain-text approach for adding classes, IDs, styles, and metadata to standard Markdown elements. Both extensions are available when you need them; plain CommonMark and GFM continue to work unchanged.
First-Class Slots
Components can accept rich Markdown in named regions. #slotname markers let you compose full Markdown into each slot without falling back to HTML or framework templates:
::hero
#title
Build UIs from **Markdown**
#description
Comark parses component syntax at runtime: no compiler, no rebuild.
Renders to HTML, ANSI, Vue, React, Svelte, and Angular.
#cta
[Get started](/getting-started)
::This is still Markdown. Readable in a text editor, parseable by a machine, renderable by any framework.
Markdown gives humans and machines a shared language. Comark gives that language one pipeline: parse once, render anywhere, and stream in real time.
Comark vs Markdoc
Comark and Markdoc both treat Markdown as data with runtime parsing. Comark adds streaming auto-close, a more compact AST, and renderers for Vue, Svelte, Angular, and ANSI.
Migration from MDC
Step-by-step guide for moving from @nuxtjs/mdc to Comark, covering package mapping, API changes, component registration, and renderer setup.