Quick Start
Five steps to a working content layer: install the package, create your Content instance, mount the handler on a route, create the client, and render your first page. No build step, no folder convention.
Install Comark Content
pnpm add comark-contentnpm install comark-contentyarn add comark-contentbun add comark-contentcomark, so you don't need to install it separately unless you want to pin a specific version.Create your Content instance
Wire the Content instance once, in a server file you own. The minimal setup is a single source pointing at your Markdown directory:
import { comarkContent } from 'comark-content'
import fs from 'comark-content/sources/fs'
export const content = comarkContent({
source: fs('./content'),
})comarkContent() returns immediately. The first read initialises the manifest lazily, so there is no build step to run.
server/content.ts in Nuxt and Nitro, src/lib/content.ts (with import 'server-only') in Next.js, src/lib/server/content.ts in SvelteKit. The integration guides cover each layout.Mount the handler
content.handler(request) is a single web-standard function, Request in and Response out, serving get, list, and navigation over HTTP. Mount it on a catch-all route under /api/content:
import { content } from '../../content'
export default defineEventHandler((event) => content.handler(toWebRequest(event)))import { content } from '@/lib/content'
export const GET = (request: Request) => content.handler(request)import { content } from '$lib/server/content'
export const GET = ({ request }) => content.handler(request)import { toWebRequest } from 'h3'
import { content } from '../../content'
export default defineEventHandler((event) => content.handler(toWebRequest(event)))import { Hono } from 'hono'
import { content } from './content'
const app = new Hono()
app.all('/api/content/*', (c) => content.handler(c.req.raw))import { defineConfig } from 'vite'
import comark from 'comark-content/vite'
import { content } from './content'
export default defineConfig({
plugins: [comark({ content })],
})Nothing to mount: the Vite plugin serves the handler at /api/content for you.
content and call content.get() directly in-process.Create the client
createContentClient() is a thin, browser-safe client for the handler you just mounted. Same method names, same return shapes — the parser and your content stay on the server:
import { createContentClient } from 'comark-content/client'
export const content = createContentClient()
// Same read API as the server instance:
// content.get('/'), content.list(), content.navigation()It defaults to same-origin requests against /api/content; pass baseURL or basePath if your handler lives elsewhere.
Render your first page
Comark Content returns a parsed MarkdownDocument to parse on the server leverage caching for maximum speed and minimal bundle size.
Install the renderer for your framework and use it in the page:
pnpm add @comark/vue<script setup lang="ts">
import { MarkdownDocument } from '@comark/vue'
import { content } from './content-client'
const page = await content.get('/')
</script>
<template>
<MarkdownDocument v-if="page" :value="page" />
</template>pnpm add @comark/reactimport { MarkdownDocument } from '@comark/react'
import { content } from './content-client'
export default async function Page() {
const page = await content.get('/')
if (!page) return null
return <MarkdownDocument value={page} />
}pnpm add @comark/svelteSee the Svelte renderer on comark.dev.
pnpm add @comark/angularSee the Angular renderer on comark.dev.
pnpm add @comark/htmlSee the HTML renderer on comark.dev.
Each entry is fully typed from its frontmatter: page.data is the frontmatter, page.nodes the parsed body.
content.get() typed from your frontmatter with no manual annotation? Run comark-content prepare (or use the Vite plugin, which does it for you) — see Type Safety.Framework guides
Step-by-step setup for your framework, following its conventions end to end:
Sources
A source tells the Content instance where your Markdown lives. The setup above reads from the filesystem, but you can load content from anywhere:
GitHub
Unstorage
Custom
Source interface to load content from a database, a SaaS API, or a custom protocol.Storage and caching
The minimal setup above reparses Markdown on every get().
It can be enough for small prerendered sites but if you need to persist parsed content, speed up reads, and query or search across your collection, add an optional layer: