---
title: "JSON plugin"
description: "Handle `.json` files as data documents to query with `content.get`, `content.list`, and SQL."
canonical_url: "https://content.comark.dev/plugins/built-in/json"
---
# JSON plugin

> Handle \`.json\` files as data documents to query with \`content.get\`, \`content.list\`, and SQL.

The `json` plugin lets your sources serve plain JSON files alongside Markdown. Each `.json` file becomes a [`ContentFile`](https://content.comark.dev/reference/types/content#contentfiletdata-tmeta) whose `data` is the parsed JSON object and whose `nodes` are empty.

## Usage

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

const content = comarkContent({
  source: fs('./content'),
  plugins: [json()],
})
```

Given `content/teams/engineering.json`:

```json [engineering.json]
{
  "name": "Engineering",
  "lead": "Ada Lovelace",
  "members": 42
}
```

```ts [query.ts]
const team = await content.get('/teams/engineering')
//    ^? ContentFile
team?.data  // { name: 'Engineering', lead: 'Ada Lovelace', members: 42 }
```

---

## API

One factory covers the whole plugin surface:

### `json(options?)`{lang="ts"}

Returns a [`ContentPlugin`](https://content.comark.dev/reference/types/plugins#contentplugin) that registers a parser for `.json` files. Each file becomes a `kind: 'document'` [`ContentFile`](https://content.comark.dev/reference/types/content#contentfiletdata-tmeta) whose `data` is the parsed JSON object and whose `nodes` are empty. Files that fail to parse, or that parse to an array (rather than an object), are handled according to [`onError`](#options-onerror) — by default they are dropped with a warning.

**Parameters:**

- `options?`: see [Options](#options).

**Returns:** [`ContentPlugin`](https://content.comark.dev/reference/types/plugins#contentplugin)

---

## Options

Both options are optional:

| Option                                    | Type                            | Default           | Description                                             |
| ----------------------------------------- | ------------------------------- | ----------------- | ------------------------------------------------------- |
| [`listingFields`](#options-listingfields) | `string[]`                      | `undefined`       | `data` fields kept in listings for `.json` entries.     |
| [`onError`](#options-onerror)             | `'throw' \| 'warn' \| 'ignore'` | Content `onError` | How to react to a malformed or non-object `.json` file. |

### `listingFields`

By default a listing keeps a document's whole `data` object. Pass `listingFields` to limit which `data` keys are retained in [`content.list()`](https://content.comark.dev/reference/content/list) results for `.json` entries, forwarded to [`content.addListingFields()`](https://content.comark.dev/reference/content/add-listing-fields):

```ts [listing-fields.ts]
import json from 'comark-content/plugins/json'

json({
  listingFields: ['name', 'members'],
})
```

**Default:** `undefined`

### `onError`

How the parser reacts when a `.json` file can't be parsed or isn't an object: `'warn'` logs and drops it, `'ignore'` drops it silently, `'throw'` aborts. Defaults to the Content-level [`onError`](https://content.comark.dev/reference/content/comark-content#options-onerror) (which defaults to `'warn'`).

```ts
json({ onError: 'throw' }) // fail fast on malformed JSON
```

**Default:** the Content instance `onError` option

---

## Read data

Each `.json` file resolves to a regular `kind: 'document'` [`ContentFile`](https://content.comark.dev/reference/types/content#contentfiletdata-tmeta) whose `data` is the parsed object, so the core content methods ([`content.get()`](https://content.comark.dev/reference/content/get), [`content.list()`](https://content.comark.dev/reference/content/list), [`content.stat()`](https://content.comark.dev/reference/content/stat), and [`content.navigation()`](https://content.comark.dev/reference/content/navigation)) treat them like any other document, and [`content.query()`](https://content.comark.dev/plugins/built-in/sql-query) indexes their flattened `data`.

```ts
await content.list()
//    → includes engineering.json alongside .md files

await content.query().where('data.members', '>', 10).all()
//    → SQL queries against JSON data work too
```

::warning
[`content.search()`](https://content.comark.dev/plugins/built-in/full-text-search) is the only method that skips JSON files. It only indexes parsed nodes, which data files don't have.
::

Markdown files give you content and structure, while JSON files give you data. The Content instance treats both as `ContentFile`, so navigation, queries, and snapshots include them uniformly.

## Trim listing payloads

When a source serves large JSON records, restrict listings to the fields you actually render:

```ts [content.ts]
const content = comarkContent({
  source: fs('./content'),
  plugins: [json({ listingFields: ['name', 'members'] })],
})

await content.list()
//    → each JSON entry's data is trimmed to { name, members }
```


## Sitemap

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