---
title: "Content layer for building Markdown-driven websites"
description: "Comark Content turns Markdown from a local folder, a GitHub repository, or any storage driver into a queryable, searchable, type-safe API. It works with your stack: Nuxt, Next.js, SvelteKit, Vite, Nitro, Node, or Hono, on your server, as a snapshot on serverless hosts, or in the browser."
canonical_url: "https://content.comark.dev"
---
# Content layer for building Markdown-driven websites

> Comark Content turns Markdown from a local folder, a GitHub repository, or any storage driver into a queryable, searchable, type-safe API. It works with your stack: Nuxt, Next.js, SvelteKit, Vite, Nitro, Node, or Hono, on your server, as a snapshot on serverless hosts, or in the browser.

::landing-hero{orientation="horizontal"}
```ts [content.ts]
import { comarkContent } from 'comark-content'
import fs from 'comark-content/sources/fs'

export const content = comarkContent({
  source: fs('./content'), // a folder of .md files
})

// reads and parse content/about.md
const page = await content.get('/about')
page.data.title // 'About', from the frontmatter
page.nodes      // the body, ready to render

// built from your folders
const nav = await content.navigation()
```

#title
Your Content,   
 as a [typed API]{.text-muted}.

#description
Point Comark Content at a folder, a GitHub repository, or a storage bucket. Read pages, list them, build navigation. Add types, search, and queries when you need them. No build step.

#links
  :::button{to="https://content.comark.dev/getting-started/installation" size="lg" trailing-icon="i-lucide-arrow-right"}
  Get started
  :::

  :::button
  ```yaml [props]
  icon: i-simple-icons-github
  color: neutral
  variant: ghost
  size: lg
  to: https://github.com/comarkdown/comark-content
  target: _blank
  ```
  View on GitHub
  :::
::

::landing-stack
```yaml [props]
items:
  - icon: i-logos-nuxt-icon
    label: Nuxt
    to: /integrations/nuxt
  - icon: i-logos-nextjs-icon
    label: Next.js
    to: /integrations/nextjs
  - icon: i-logos-svelte-icon
    label: SvelteKit
    to: /integrations/sveltekit
  - icon: i-logos-vite-icon
    label: Vite
    to: /integrations/vite
  - icon: i-unjs-nitro
    label: Nitro
    to: /integrations/nitro
  - icon: i-logos-nodejs-icon
    label: Node
    to: /integrations/node
  - icon: i-logos-hono
    label: Hono
    to: /integrations/hono
```
Works with your stack
::

::landing-tabs
```yaml [props]
items:
  - icon: i-lucide-file-text
    title: 1. Read a file
    description: One Markdown file, one call. No build step, no database, no framework required.
  - icon: i-lucide-monitor
    title: 2. Render it
    description: One renderer package for your framework turns the parsed body into components.
  - icon: i-lucide-trending-up
    title: 3. Grow when you need to
    description: Types from your frontmatter, full-text search, several sources, snapshots for serverless. Each one is a line you add later.
```
#headline
Start small

#title
One file today. Everything else when you need it.

#description
Docs sites, blogs, changelogs, knowledge bases, content for LLM pipelines. The first result takes a few minutes, and nothing you add later changes how you read a page.

#code-0
```md [content/about.md]
---
title: About
---

# About us

We write documentation for a living.
```

```ts [index.ts]
const page = await content.get('/about')
console.log(page.data.title) // About
```

#code-1
```vue [app/pages/[...slug\\].vue]
<script setup lang="ts">
import { MarkdownDocument } from '@comark/vue'

const route = useRoute()
const { data: page } = await useAsyncData(() => {
  return contentClient.get(route.path)
})
</script>

<template>
  <MarkdownDocument v-if="page" :value="page" />
</template>
```

#code-2
```ts [content.ts]
import sqlite from 'comark-content/database/sqlite-node'
import sqliteFullTextSearch from 'comark-content/plugins/sqlite-full-text-search'

export const content = comarkContent({
  source: fs('./content'),
  plugins: [
    sqliteFullTextSearch({ database: sqlite() })
  ],
})

// npx comark-content prepare
//   -> page.data is typed from your frontmatter
// npx comark-content snapshot
//   -> ship parsed content to serverless hosts
```
::

::landing-tabs
```yaml [props]
reverse: true
items:
  - icon: i-lucide-folder-tree
    title: Filesystem
    description: A folder next to your app. The fastest dev loop, with edits reflected as you save.
  - icon: i-simple-icons-github
    title: GitHub
    description: A repository, branch, and folder. Writers push Markdown; your app reads it without a redeploy.
  - icon: i-lucide-database-zap
    title: 20+ storage drivers
    description: S3, Cloudflare KV, Redis, HTTP, or any other unstorage driver.
```
#headline
Sources

#title
Bring your own source

#description
Content can live next to your app, in a separate repository, or in any storage driver. Same API either way. This site is the proof: its pages live in a [GitHub repository](https://github.com/comarkdown/comark-content/tree/main/docs/content) and go live on push, with a preview per branch.

#code-0
```ts [content.ts]
import { comarkContent } from 'comark-content'
import fs from 'comark-content/sources/fs'

const content = comarkContent({
  source: fs('./content'),
})

const page = await content.get('/getting-started')
```

#code-1
```ts [content.ts]
import { comarkContent } from 'comark-content'
import github from 'comark-content/sources/github'

const content = comarkContent({
  source: github({
    repo: 'nuxt/nuxt',
    branch: 'main',
    path: 'docs',
  }),
})

const page = await content.get('/getting-started')
```

#code-2
```ts [content.ts]
import { comarkContent } from 'comark-content'
import unstorageSource from 'comark-content/sources/unstorage'
import s3Driver from 'unstorage/drivers/s3'

const content = comarkContent({
  source: unstorageSource({
    driver: s3Driver({
      bucket: 'acme-content',
      region: 'us-east-1',
    }),
  }),
})

const page = await content.get('/getting-started')
```
::

::landing-tabs
```yaml [props]
items:
  - icon: i-lucide-shield-check
    title: Typed get & list
    description: Known paths narrow automatically. page.data is your frontmatter shape, no type parameter.
  - icon: i-lucide-database
    title: SQL query
    description: A fluent builder over frontmatter columns. Filter, sort, and paginate without writing SQL.
  - icon: i-lucide-search
    title: Full-text search
    description: BM25-ranked sections with highlighted snippets, built on SQLite FTS5.
```
#headline
Typed API

#title
Typed, searchable, queryable

#description
Types are generated from your actual frontmatter and narrow `get`, `list`, and `query` by instance and by path. Add SQLite when you want SQL queries and full-text search.

#code-0
```ts
const content = comarkContent({
  source: fs('./content'),
})

// Known path -> data auto-narrowed, no type parameter
const blogPost = await content.get('/posts/hello')

blogPost?.data.title // typed from your frontmatter
blogPost?.data.tags  // autocompletes every field

const pages = await content.list()
// Every document in the instance
```

#code-1
```ts
const posts = comarkContent('posts', {
  source: fs('./content/posts'),
  plugins: [
    sqlQuery({ database: sqlite() })
  ],
})

const featured = await posts.query()
  .where('data.featured', '=', true)
  .order('data.published', 'DESC')
  .limit(10)
  .all()

featured[0].data.title // same nested shape as posts.list()
```

#code-2
```ts
const content = comarkContent({
  source: fs('./content'),
  plugins: [
    sqliteFullTextSearch({ database: sqlite() })
  ],
})

const hits = await content.search('markdown parser', {
  limit: 5,
  snippet: { tag: 'mark' },
})

hits[0].title            // matching section heading
hits[0].snippets.content // highlighted excerpt, BM25-ranked
```
::

::landing-features
#headline
Capabilities

#title
A small core. Everything else is a plugin.

#default
  :::landing-feature-card{icon="i-lucide-blocks" to="https://content.comark.dev/integrations/nuxt"}
  #title
  Works with your stack

  #description
  Nuxt, Next.js, Vite, SvelteKit, Nitro, Node, or Hono: mount one handler, render with Vue, React, or Svelte.
  :::

  :::landing-feature-card{icon="i-lucide-images" to="https://content.comark.dev/plugins/built-in/media"}
  #title
  Documents and assets, together

  #description
  Images and files live next to your Markdown: query them like content, serve them where they are referenced.
  :::

  :::landing-feature-card{icon="i-lucide-shield-check" to="https://content.comark.dev/guide/typescript"}
  #title
  Type-safe from your frontmatter

  #description
  Generated types narrow `get()` and `query()` by instance and by path. Run one CLI command, or let the Vite plugin generate them.
  :::

  :::landing-feature-card{icon="i-lucide-feather" to="https://content.comark.dev/reference/content/comark-content"}
  #title
  Lightweight core

  #description
  Three methods: get, list, navigation. Plugins cover the rest.
  :::

  :::landing-feature-card{icon="i-lucide-puzzle" to="https://content.comark.dev/plugins"}
  #title
  Opt-in SQL and search

  #description
  Skip the database for small sites. Add SQLite through a plugin when you want SQL queries and BM25 full-text search.
  :::

  :::landing-feature-card{icon="i-lucide-list-tree" to="https://content.comark.dev/guide/navigation"}
  #title
  Navigation for free

  #description
  content.navigation() builds an ordered, nested tree from your files: numeric prefixes, .navigation.yml, frontmatter overrides.
  :::
::

::landing-features
#headline
Compare

#title
Know what you're replacing

#default
  :::landing-feature-card{icon="i-lucide-scale" to="https://content.comark.dev/compare/comark-content-vs-contentlayer"}
  #title
  vs Contentlayer

  #description
  Runtime parsing from any source vs an unmaintained build-time compiler coupled to your bundler.
  :::

  :::landing-feature-card{icon="i-lucide-scale" to="https://content.comark.dev/compare/comark-content-vs-astro-content-collections"}
  #title
  vs Astro Content Collections

  #description
  The same typed collections, without the two constraints: any framework, no rebuild on content changes.
  :::

  :::landing-feature-card{icon="i-lucide-scale" to="https://content.comark.dev/compare/comark-content-vs-headless-cms"}
  #title
  vs Headless CMS

  #description
  Markdown in storage you own, turned into the same queryable API — no hosted database, no vendor API.
  :::
::

::landing-faq
```yaml [props]
items:
  - label: What is Comark Content?
    content: An open source content layer for Markdown. It reads Markdown from the filesystem, GitHub, or any storage driver and exposes it through a typed API — get, list, navigation — with optional SQL queries and full-text search. It is a library, not a hosted service.
  - label: How is it different from Contentlayer or Astro Content Collections?
    content: Those run at build time inside one framework, so every content change requires a rebuild. Comark Content parses content at runtime from any source and works with Nuxt, Next.js, SvelteKit, Vite, Nitro, Node, Hono, or the browser. With content in a GitHub repository, a push can reach production without a rebuild; how fast depends on the caching and refresh setup you choose.
  - label: Do I need a database?
    content: No. The core works with a source and an in-memory cache. Add SQLite (node or WASM) only when you want SQL queries over frontmatter or BM25 full-text search.
  - label: Where can my content live?
    content: On the filesystem next to your app, in a GitHub repository, in any unstorage driver (S3, Cloudflare KV, Redis, HTTP), or behind a custom source you implement.
  - label: Which frameworks does it work with?
    content: Any. The Content instance returns a framework-neutral AST rendered by Comark packages for Vue, React, Svelte, Angular, or plain HTML. Step-by-step guides cover Nuxt, Next.js, SvelteKit, Vite, Nitro, Node, and Hono, and a Vite plugin does the wiring for single-page apps.
  - label: Is it free?
    content: Yes. MIT-licensed open source. Your only costs are your own hosting and storage.
```
#headline
FAQ

#title
Common questions
::

::landing-cta
#title
Add a content layer in five lines.

#description
One source, one `get()`, a printed title in a few minutes. Add [search and queries](https://content.comark.dev/plugins) when you need them, and write in [Comark syntax](https://comark.dev) when you need components.

#links
  :::button{to="https://content.comark.dev/getting-started/installation" trailing-icon="i-lucide-arrow-right" size="lg"}
  Start building
  :::

  :::button
  ```yaml [props]
  to: https://content.comark.dev/reference/content/comark-content
  color: neutral
  variant: outline
  size: lg
  ```
  API reference
  :::
::

## Resources for Agents

- [API catalog: every service document this site publishes](https://content.comark.dev/.well-known/api-catalog)
- [Sitemap (XML)](https://content.comark.dev/sitemap.xml)
- [Sitemap (Markdown): every page on the site](https://content.comark.dev/sitemap.md)
- [MCP server card: MCP endpoint at https://content.comark.dev/mcp](https://content.comark.dev/.well-known/mcp/server-card.json)
- [MCP endpoint (streamable HTTP)](https://content.comark.dev/mcp)
- [llms.txt: index of the documentation for LLMs](https://content.comark.dev/llms.txt)
- [llms-full.txt: the full documentation as a single file](https://content.comark.dev/llms-full.txt)
- [OpenAPI document: every route this site serves to agents](https://content.comark.dev/openapi.json)
- [RSS feed of the documentation](https://content.comark.dev/rss.xml)


## Sitemap

See the full [sitemap](https://content.comark.dev/sitemap.md) for all pages.
