Vite
Set up Comark Content in a Vite app with the official plugin.
The comark-content/vite plugin does the wiring for you: it serves content.handler() as a dev middleware, pushes content edits over HMR, generates source types, and emits static artifacts at build time so the same URLs keep working in production.
Install the packages
pnpm add comark-content @comark/vuenpm install comark-content @comark/vueyarn add comark-content @comark/vuebun add comark-content @comark/vuepnpm add comark-content @comark/reactnpm install comark-content @comark/reactyarn add comark-content @comark/reactbun add comark-content @comark/reactAdd the plugin
Create the Content instance in vite.config.ts and pass it to the plugin:
vite.config.ts
import { defineConfig } from 'vite'
import { fileURLToPath } from 'node:url'
import { comarkContent } from 'comark-content'
import fs from 'comark-content/sources/fs'
import comark from 'comark-content/vite'
const content = comarkContent({
source: fs(fileURLToPath(new URL('./content', import.meta.url))),
})
export default defineConfig({
plugins: [comark({ content })],
})Everything is on by default:
| Option | Default | Role |
|---|---|---|
server | true | Dev middleware forwarding <prefix>/* to content.handler() (plus media files at their manifest path) |
watch | true | Watches the source and forwards updates over HMR as comark:update / comark:remove events |
prefix | '/api/content' | URL prefix the middleware is mounted under |
prerender | true | Emits manifest.json, per-source snapshots, and media as static assets at build time |
types | true | Writes comark-content.d.ts on dev start and build |
Create the browser Content
A Vite SPA has no server at runtime, so instead of a thin HTTP client the app runs the whole Content instance in the browser, hydrated from the artifacts the plugin serves in dev and emits at build:
src/composables/useContent.ts
import { ref } from 'vue'
import { comarkContent, type ContentFile } from 'comark-content'
const PREFIX = '/api/content'
export const content = comarkContent({
cache: {
loadManifest: () => fetch(`${PREFIX}/manifest.json`).then((r) => (r.ok ? r.json() : null)),
loadSnapshot: (source) => fetch(`${PREFIX}/snapshot/${source}.json`).then((r) => (r.ok ? r.json() : null)),
},
})
// Bumps whenever an HMR event has patched the content.
export const version = ref(0)
if (import.meta.hot) {
import.meta.hot.on('comark:update', async ({ source, file }: { source: string; file: ContentFile }) => {
await content.update(source, file)
version.value++
})
import.meta.hot.on('comark:remove', async ({ source, key }: { source: string; key: string }) => {
await content.remove(source, key)
version.value++
})
}src/content.ts
import { comarkContent, type ContentFile } from 'comark-content'
const PREFIX = '/api/content'
export const content = comarkContent({
cache: {
loadManifest: () => fetch(`${PREFIX}/manifest.json`).then((r) => (r.ok ? r.json() : null)),
loadSnapshot: (source) => fetch(`${PREFIX}/snapshot/${source}.json`).then((r) => (r.ok ? r.json() : null)),
},
})
if (import.meta.hot) {
import.meta.hot.on('comark:update', async ({ source, file }: { source: string; file: ContentFile }) => {
await content.update(source, file)
})
import.meta.hot.on('comark:remove', async ({ source, key }: { source: string; key: string }) => {
await content.remove(source, key)
})
}The HMR handlers keep the in-browser manifest in sync while you edit content — no page reload.
Render a page
src/App.vue
<script setup lang="ts">
import { MarkdownDocument } from '@comark/vue'
import { content } from './composables/useContent'
const page = await content.get('/')
</script>
<template>
<MarkdownDocument v-if="page" :value="page" />
</template>src/App.tsx
import { useEffect, useState } from 'react'
import { MarkdownDocument } from '@comark/react'
import type { ContentFile } from 'comark-content'
import { content } from './content'
export default function App() {
const [page, setPage] = useState<ContentFile | null>(null)
useEffect(() => {
content.get('/').then((page) => {
if (page) setPage(page)
})
}, [])
return page && <MarkdownDocument value={page} />
}Hydrating a browser-side Content from
manifest.json and snapshots is covered in depth in Artifacts & Hydration.