---
title: "comarkContent()"
description: "Create a Comark Content instance from one source, an optional cache, and plugins."
canonical_url: "https://content.comark.dev/reference/content/comark-content"
---
# comarkContent()

> Create a Comark Content instance from one source, an optional cache, and plugins.

## `comarkContent(name?, options)`

Creates a Comark Content instance from one source, an optional cache, and optional plugins. The returned type is the base instance plus whatever methods the plugins contribute.

**Parameters:**

- `name`: an optional instance name. Defaults to `default`. See [Name](#name).
- `options`: a [`ContentOptions`](https://content.comark.dev/reference/types/content#contentoptions) object. See [Options](#options).

**Returns:** `ComarkContent & <plugin methods>`, a typed handle. Base methods are always present; plugins like [`sqlQuery`](https://content.comark.dev/plugins/built-in/sql-query) add `content.query`, [`sqliteFullTextSearch`](https://content.comark.dev/plugins/built-in/full-text-search) adds `content.search`.

Store it with `typeof content`, not a hand-written `ComarkContent` annotation: the instance name is a type parameter that the bare type cannot absorb, and `comark-content prepare` does not change that. See [derive, don't annotate](https://content.comark.dev/guide/typescript#derive-the-instance-type-dont-annotate-it).

```ts [content.ts]
import { comarkContent } from 'comark-content'
import fs from 'comark-content/sources/fs'
import sqlite from 'comark-content/database/sqlite-node'
import sqlQuery from 'comark-content/plugins/sql-query'

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

content.get // base method
content.query // contributed by sqlQuery
```

## Name

An instance reads **one** source and carries a name, which defaults to `default`. The name identifies the instance everywhere its data surfaces internally. It never appears in public paths:

```ts [named.ts]
const blog = comarkContent('blog', { source: fs('./content/blog') })

blog.name // 'blog'
const post = await blog.get('/hello') // reads ./content/blog/hello.md
post?.path // '/hello': the name is not part of the path
post?.meta.key // 'blog/hello.md': the name prefixes the internal key
```

To put the blog under `/blog/…` in the URL space, set a [`prefix`](https://content.comark.dev/guide/files-and-paths#add-a-url-prefix) on the source; the name has no effect on paths.

| The name appears in                                                   | Example                                 |
| --------------------------------------------------------------------- | --------------------------------------- |
| Each entry's `meta.key`                                               | `blog/hello.md`                         |
| Cache keys                                                            | `blog:hello.md`                         |
| The [generated registry](https://content.comark.dev/guide/typescript) | `ContentRegistry['blog']`               |
| Snapshot routes and files                                             | `/snapshot/blog.json`, `.content/blog/` |
| Query index tables                                                    | `__search_blog`                         |

Name an instance whenever you run more than one, since those namespaces must not collide. A single-instance application can leave it out.

### Valid names

A name is an identifier: letters, digits and underscores, starting with a letter or underscore (`/^[A-Za-z_][A-Za-z0-9_]*$/`). Anything else throws at construction.

```ts [names.ts]
comarkContent('local_content', …) // ok
comarkContent('v1_2', …) // ok
comarkContent('my-blog', …) // Error: invalid instance name
comarkContent('v1.2', …) // Error: invalid instance name
```

One rule covers every place the name lands. `my-blog` and `my_blog` would otherwise both generate a `MyBlogData` type; `v1.2` could not be addressed as `/snapshot/v1.2.json`. The generated type prefix is the PascalCase of the name: `local_content` becomes `LocalContentData` and `LocalContentRow`.

::note
To serve several bodies of content from one application, create one instance per source and compose them with [`contentHub()`](https://content.comark.dev/reference/content-hub). Each instance keeps its own cache policy and plugins — see [Combine content sources](https://content.comark.dev/advanced/hub).
::

## Options

`comarkContent()` accepts the following options:

| Option                          | Type                                                                                         | Default          | Description                                                                                |
| ------------------------------- | -------------------------------------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------ |
| [`source`](#options-source)     | [`ContentSource`](https://content.comark.dev/reference/types/content#contentsource)          | `undefined`      | The source this instance reads.                                                            |
| [`cache`](#options-cache)       | [`CacheOptions`](https://content.comark.dev/reference/types/content#cacheoptions)` \| false` | `{}`             | Where parsed entries are stored and kept fresh. In-memory by default; `false` disables it. |
| [`plugins`](#options-plugins)   | `ContentPlugin[]`                                                                            | `[]`             | Plugins to install at construction.                                                        |
| [`markdown`](#options-markdown) | `ParserOptions`                                                                              | `undefined`      | Forwarded to the Comark parser (markdown plugins, options).                                |
| [`basePath`](#options-basepath) | `string`                                                                                     | `'/api/content'` | Path [`handler()`](https://content.comark.dev/reference/content/handler) is mounted at.    |
| [`logger`](#options-logger)     | [`Logger`](https://content.comark.dev/reference/types/content#logger)` \| false`             | `console`        | Where pipeline diagnostics go. Pass a custom logger, or `false` to silence output.         |
| [`onError`](#options-onerror)   | `'throw' \| 'warn' \| 'ignore'`                                                              | `'warn'`         | Default reaction when a file fails to parse.                                               |

### `source`

The source this instance reads, mounted under the instance [name](#name).

```ts
const content = comarkContent({ source: fs('./content') })
const post = await content.get('/posts/hello') // reads ./content/posts/hello.md
post?.meta.key // 'default/posts/hello.md': the instance name plus the file's location in the source
```

Either a **raw** source that hands over file text ([filesystem](https://content.comark.dev/sources/filesystem), [GitHub](https://content.comark.dev/sources/github), [unstorage](https://content.comark.dev/sources/unstorage), [custom](https://content.comark.dev/sources/custom)), or a **parsed** source that hands over content Comark already parsed ([snapshot](https://content.comark.dev/sources/snapshot), or a CMS API):

```ts [parsed.ts]
// No filesystem at runtime: the stored snapshot is the origin.
comarkContent({ source: snapshot('/api/content/snapshot/default.json') })
```

`source` is required. `comarkContent()` throws when it is missing, rather than building an instance
with nothing to read:

```ts [throws.ts]
comarkContent({ plugins: [json()] })
// Error: comarkContent: "default" has no source. Pass one `source` …
```

A [parsed source](https://content.comark.dev/sources/snapshot) counts, but it still has to be able to do something: an instance
that can neither enumerate entries nor read bodies throws too.

### `cache`

Parsed entries are stored in a cache backed by an [unstorage](https://unstorage.unjs.io) driver: in-memory by default, or any driver you provide. Pass `false` to disable caching entirely, so every [`get()`](https://content.comark.dev/reference/content/get) re-parses from the source.

| Field    | Type      | Default     | Description                                                             |
| -------- | --------- | ----------- | ----------------------------------------------------------------------- |
| `driver` | `Driver`  | in-memory   | unstorage driver backing the cache.                                     |
| `ttl`    | `number`  | `undefined` | Milliseconds before an entry is considered stale. Omit to never expire. |
| `swr`    | `boolean` | `true`      | Serve a stale entry while it revalidates in the background.             |

See [Configure caching](https://content.comark.dev/advanced/caching) and [Artifacts and hydration](https://content.comark.dev/advanced/artifacts-and-hydration).

### `plugins`

Plugins run synchronously at construction. They register parsers, hook into events, and add methods to the returned Content, fully typed.

```ts
comarkContent({
  source: fs('./content'),
  plugins: [sqlQuery({ database }), sqliteFullTextSearch({ database })],
})
```

See the [plugin authoring API](https://content.comark.dev/reference/plugins/define-content-plugin) and the [built-in plugins](https://content.comark.dev/plugins).

### `markdown`

Forwarded verbatim to the automatically installed [`markdown` plugin](https://content.comark.dev/plugins/built-in/markdown). Use it to add Markdown-level plugins (highlight, toc, emoji, math, mermaid) and toggle parser options.

```ts
import highlight from 'comark/plugins/highlight'

comarkContent({
  source: fs('./content'),
  markdown: { plugins: [highlight()], autoClose: true },
})
```

If you add `markdown()` to `plugins` yourself—for example, to configure `listingFields`—pass these options to `markdown({ comark })` instead. The top-level value only configures the automatic fallback plugin.

See [Comark parse options](https://comark.dev/api/parse) for the full reference.

### `basePath`

The path [`handler()`](https://content.comark.dev/reference/content/handler) is mounted at. The handler strips this prefix from the request pathname before dispatching. Defaults to `/api/content`.

### `logger`

The logger used across the Content instance pipeline and its plugins. By default, diagnostics (such as a file that failed to parse, or a schema-validation warning) are written to `console` with a `[comark-content]` prefix.

Pass your own [`Logger`](https://content.comark.dev/reference/types/content#logger) to redirect output to your app's logging system, or pass `false` to silence it entirely — useful in environments that strip `console` from production builds, or in tests.

```ts
import { consola } from 'consola'

// Route through your own logger
comarkContent({
  source: fs('./content'),
  logger: {
    debug: (...a) => consola.debug(...a),
    info: (...a) => consola.info(...a),
    warn: (...a) => consola.warn(...a),
    error: (...a) => consola.error(...a),
  },
})

// Silence all pipeline output
comarkContent({ source: fs('./content'), logger: false })
```

The active logger is available as [`content.logger`](#instance) and is passed to plugins and parsers via their context.

### `onError`

How the pipeline reacts when a file fails to parse — for example malformed JSON/YAML, or a markdown field that can't be parsed. Applies to the built-in `json`, `yaml`, and `markdown-fields` plugins.

| Value                | Behaviour                                                                  |
| -------------------- | -------------------------------------------------------------------------- |
| `'warn'` *(default)* | Log a warning via [`logger`](#options-logger) and drop the offending file. |
| `'ignore'`           | Silently drop the offending file.                                          |
| `'throw'`            | Surface the error and abort the operation (for example `init()` rejects).  |

```ts
// Fail fast on any bad content file
comarkContent({ source: fs('./content'), onError: 'throw' })
```

Individual plugins accept their own `onError` to override this default per file type:

```ts
comarkContent({
  source: fs('./content'),
  onError: 'warn',
  plugins: [json({ onError: 'throw' })], // stricter for JSON only
})
```

Schema-level validation is handled separately by the [`schema-validation`](https://content.comark.dev/plugins/built-in/schema-validation) plugin's `onError` option.

::note{to="https://content.comark.dev/plugins/built-in/tracing"}
To time the content lifecycle (debug timelines locally or OpenTelemetry span forwarding in production) install one of the **tracing plugins** (`tracingDebug` / `tracingOtel`).
::

## Instance

The returned instance exposes these methods:

- [`content.get(path, options?)`](https://content.comark.dev/reference/content/get): read one document by its public path or file key.
- [`content.list()`](https://content.comark.dev/reference/content/list): list lightweight entries from the manifest.
- [`content.navigation()`](https://content.comark.dev/reference/content/navigation): build a navigation tree.
- [`content.stat(key)`](https://content.comark.dev/reference/content/stat): look up a single manifest entry.
- [`content.getSource()`](https://content.comark.dev/reference/content/get-source): the raw source, when the instance has one.
- [`content.init(options?)`](https://content.comark.dev/reference/content/init): initialize the instance.
- [`content.manifest()`](https://content.comark.dev/reference/content/manifest): the saveable index, without bodies.
- [`content.snapshot()`](https://content.comark.dev/reference/content/snapshot): every parsed file, bodies included.
- [`content.refresh()`](https://content.comark.dev/reference/content/refresh): re-read the source and reconcile the index.
- [`content.update(file)`](https://content.comark.dev/reference/content/update): store a single already-parsed file.
- [`content.ingest(file)`](https://content.comark.dev/reference/content/ingest): run the parse pipeline on a hand-built file, then store it.
- [`content.remove(key)`](https://content.comark.dev/reference/content/remove): drop a single entry.
- [`content.watch()`](https://content.comark.dev/reference/content/watch): keep the manifest and cache in sync with file changes.
- [`content.handler(request)`](https://content.comark.dev/reference/content/handler): handle a web-standard request.
- [`content.addParser(extensions, parse)`](https://content.comark.dev/reference/content/add-parser): register a parser for file extensions.
- [`content.addListingFields(extensions, fields)`](https://content.comark.dev/reference/content/add-listing-fields): limit which fields listings keep.
- [`content.addServeHandler(section, handler)`](https://content.comark.dev/reference/content/add-serve-handler): register a custom handler section.

And these properties:

- `content.name`: the instance [name](#name).
- [`content.cache`](https://content.comark.dev/reference/content/cache): the resolved [`Cache`](https://content.comark.dev/reference/types/content#cache) backing the instance.
- `content.logger`: the resolved [`Logger`](https://content.comark.dev/reference/types/content#logger) used across the pipeline.
- [`content.hooks`](https://content.comark.dev/reference/content/hooks): a [`Hookable`](https://github.com/unjs/hookable) bus for content-change events.
- [`content.perf`](https://content.comark.dev/reference/content/perf): the lifecycle timing recorder, a no-op unless a [tracing plugin](https://content.comark.dev/plugins/built-in/tracing) is installed.
- [`content.status`](https://content.comark.dev/reference/content/status): the lifecycle status (`'created'`, `'initializing'`, `'initialized-partial'`, or `'initialized-full'`).

## Usage

```ts [server/utils/content.ts]
import { comarkContent } from 'comark-content'
import fs from 'comark-content/sources/fs'

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


## Sitemap

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