Comark Content vs Contentlayer

Contentlayer compiles local content into typed JSON at build time. Comark Content reads Markdown from any source at runtime. Compare architecture, type safety, sources, and maintenance status.

TL;DR: Both give you typed access to Markdown content. Contentlayer compiles local files into JSON at build time, is coupled to your bundler, and has been unmaintained since 2023. Comark Content parses Markdown at runtime from any source (filesystem, GitHub, S3, KV, custom), so content updates don't require a rebuild, and it adds SQL queries and full-text search when you need them.

At a glance

Comark ContentContentlayer
MaintenanceActiveUnmaintained since 2023 (community fork: contentlayer2)
When content is processedRuntime (or build time via snapshots)Build time only
Content sourcesFilesystem, GitHub, any unstorage driver, customLocal files
Content updateLive, no rebuildRebuild and redeploy
FrameworksAny: Nuxt, Next.js, Vite, Nitro, Node, browserNext.js (official), others partial
Type safetyTypes generated from your contentTypes generated from a schema you declare
Markdown flavorCommonMark + GFM + components in MarkdownMarkdown / MDX
Queryinglist() filters, optional SQL queriesImport generated arrays (allPosts), filter in JS
SearchOptional BM25 full-text searchNone
Where it runsServer, edge, browserBuild process only

The core difference

Contentlayer is a build step. It watches content/, validates files against a schema, and emits typed JSON your app imports:

// Contentlayer: data was generated at build time
import { allPosts } from 'contentlayer/generated'

const post = allPosts.find((p) => p.slug === 'hello')

Every content change requires a rebuild. Content that isn't a local file (a separate repo, a bucket, an API) is out of scope.

Comark Content is a runtime layer. It reads from a source when your app asks:

// Comark Content: read at request time
import { comarkContent } from 'comark-content'
import github from 'comark-content/sources/github'

const content = comarkContent({
  source: github({ repo: 'my-org/content', branch: 'main' }),
})

const post = await content.get('/blog/hello')

Editors push Markdown, the next request serves it. No rebuild, no redeploy. If you prefer build-time semantics, parse once and ship a snapshot: you keep the typed API without the runtime source.

Migrating from Contentlayer

Contentlayer's repository stopped receiving updates in 2023. If you're moving off it:

  1. Document types → sources. A defineDocumentType per folder becomes one source: fs('./content'). Frontmatter fields are picked up as-is, or declare a JSON Schema per source via its schema option for validation and typed get() / list().
  2. allPostslist(). Generated arrays become await content.list(), filtered by path or frontmatter. For structured queries, add the SQL query plugin.
  3. MDX components → Comark components. <Alert type="info"> becomes ::alert{type="info"} — plain text, no compilation, rendered with your framework's <MarkdownDocument>.
  4. .mdx compilation → runtime parse. There is no compile step to configure. content.get() returns an AST your renderer consumes directly.

FAQ

Is Contentlayer still maintained?

No. The original contentlayer package has had no releases since 2023. A community fork, contentlayer2, continues maintenance for Next.js users.

Can Comark Content work at build time like Contentlayer?

Yes. Parse your source once, serialize a snapshot, and hydrate it at runtime — including fully in the browser. You get build-time determinism with the same API.

Does Comark Content validate frontmatter like Contentlayer schemas?

Yes. Pass a JSON Schema to a source via its schema option. Documents are validated on load, and the schema drives generated types and SQL query columns.