Next.js
With the App Router, server components read the Content instance in-process: no route, no client, just await content.get(). The handler and browser client only come in when client components need to fetch content.
Install the packages
pnpm add comark-content @comark/react server-onlynpm install comark-content @comark/react server-onlyyarn add comark-content @comark/react server-onlybun add comark-content @comark/react server-onlyCreate the Content instance
Mark the module server-only so the parser and your content never leak into a client bundle:
import 'server-only'
import { comarkContent } from 'comark-content'
import fs from 'comark-content/sources/fs'
export const content = comarkContent({
source: fs('./content'),
})Render a page from a server component
Server components call the Content instance directly, no HTTP round-trip involved:
import { MarkdownDocument } from '@comark/react'
import { notFound } from 'next/navigation'
import { content } from '@/lib/content'
export default async function Page() {
const page = await content.get('/')
if (!page) notFound()
return <MarkdownDocument value={page} />
}Map Markdown components to your own React components through the components prop, see the React renderer.
Optional: expose the handler for client components
If client components need to read content, mount content.handler() on a route handler — it already speaks web-standard Request/Response:
import { content } from '@/lib/content'
export const GET = (request: Request) => content.handler(request)Then consume it with the browser client, same read API as the server instance:
import { createContentClient } from 'comark-content/client'
export const contentClient = createContentClient()
// contentClient.get('/'), contentClient.list(), contentClient.navigation()Generate source types
Type content.get() from your frontmatter by generating source types:
npx comark-content prepareAdd comark-content.d.ts to your tsconfig.json include, see Type Safety. As well as your .gitignore.
content.watch() from src/lib/content.ts so content edits invalidate the manifest without restarting the dev server, the Next example shows the Turbopack-safe wiring.