Quick Start

Install Comark Content, mount the handler, and render your first page.

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-content
The package ships its own dependency on comark, 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:

content.ts
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.

Place the file where your framework keeps server-only code: 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:

server/api/content/[...path].ts
import { content } from '../../content'

export default defineEventHandler((event) => content.handler(toWebRequest(event)))
Only reading content server-side (server components, SSG, scripts)? Skip this step and the next one: import 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:

content-client.ts
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:

Terminal
pnpm add @comark/vue
App.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>

Each entry is fully typed from its frontmatter: page.data is the frontmatter, page.nodes the parsed body.

You now have a working Content 🚀
Want 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:

Nuxt

Server instance, catch-all API route, and a $fetch-aware client.

Next.js

Server components in-process, or a route handler for client components.

Vite

The comark-content/vite plugin: dev middleware, HMR, typegen, static artifacts.

SvelteKit

A +server.ts endpoint and the browser client.

Nitro

Serve content over HTTP from a standalone Nitro server.

Node

Read, query, and search content from a plain script.

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:

Filesystem

Mount a local directory of Markdown files. Keep your content close to your app.

GitHub

Load Markdown straight from a GitHub repository. Separate your content from your app and do not require a build step when content changes.

Unstorage

Wrap any unstorage driver: S3, Cloudflare KV, HTTP, Redis... Fetch your content where it lives.

Custom

Implement the 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:

Database

Back the SQL query and full-text search plugins with SQLite. Works server-side and in the browser.

Cache

Cache parsed bodies to fasten your reads and reduce your server load.