---
title: "Create a Comark Content Parser"
description: "Turn new file types into documents with content.addParser()."
canonical_url: "https://content.comark.dev/plugins/custom/parser-api"
---
# Create a Comark Content Parser

> Turn new file types into documents with content.addParser().

By default the Content instance only parses Markdown. But [`content.addParser()`](#contentaddparserextensions-parse) handles any extension.

::note
The built-in [`json`](https://content.comark.dev/plugins/built-in/json) and [`yaml`](https://content.comark.dev/plugins/built-in/yaml) plugins are thin wrappers over this API.
::

## `content.addParser(extensions, parse)`

Registers a [`Parser`](#parser) that runs for every file with one of the given extensions.

**Parameters:**

- `extensions`: an array of dotted file extensions, for example `['.csv']`.
- `parse`: a [`Parser`](#parser) callback that turns a raw file into a [`ParserResult`](https://content.comark.dev/reference/types/content#parser).

**Returns:** `void`

---

## `Parser`

The callback `content.addParser` runs: `(ctx: ParserContext) => ParserResult | null`. It receives a [`ParserContext`](https://content.comark.dev/reference/types/content#parser) describing the file and returns a [`ParserResult`](https://content.comark.dev/reference/types/content#parser), or `null` to skip the file.

**Parameters:**

- `ctx`: a [`ParserContext`](https://content.comark.dev/reference/types/content#parser).

| Field       | Type                              | Description                                                                                                                                                                         |
| ----------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `read`      | `() => string \| Promise<string>` | Fetch the raw file contents.                                                                                                                                                        |
| `extension` | `string`                          | The file's extension, for example `.csv`.                                                                                                                                           |
| `filepath`  | `string`                          | The file's location inside the source (the [file key](https://content.comark.dev/guide/files-and-paths#internal-identifiers)), for example `teams/roster.csv`. Not the public path. |
| `partial`   | `boolean`                         | `true` on lightweight scans (manifest build): parse only metadata and skip the body.                                                                                                |
| `logger`    | `Logger`                          | Logger for parser diagnostics; respects the instance's `logger` option.                                                                                                             |
| `onError`   | `OnErrorMode`                     | The instance's default reaction to parse failures.                                                                                                                                  |

**Returns:** a [`ParserResult`](https://content.comark.dev/reference/types/content#parser) (or `null` to skip). Every field is optional.

| Field     | Type                    | Description                                                            |
| --------- | ----------------------- | ---------------------------------------------------------------------- |
| `kind`    | `'document' \| 'media'` | How to track the file. Defaults to `'document'`.                       |
| `type`    | `string`                | The file's MIME type.                                                  |
| `data`    | `ContentFile['data']`   | The parsed data (frontmatter for documents).                           |
| `meta`    | `Record<string, any>`   | Extra metadata merged onto the entry.                                  |
| `nodes`   | `ContentFile['nodes']`  | The parsed AST, empty for data-only files.                             |
| `partial` | `boolean`               | Whether only metadata was parsed. Defaults to the context's `partial`. |

::tip
To trim which `data` fields listings keep, pair the parser with [`content.addListingFields()`](https://content.comark.dev/reference/content/add-listing-fields).
::

---

## Usage

Write a parser plugin, register it on [`comarkContent()`](https://content.comark.dev/reference/content/comark-content), then read the new files like any other document.

### Define the parser

Call `content.addParser` from the plugin's `setup`. This CSV parser reads the raw file and returns each row as `data`, with empty `nodes`:

```ts [csv-plugin.ts]
import { defineContentPlugin } from 'comark-content'

export default defineContentPlugin(() => ({
  name: 'csv',
  setup(content) {
    content.addParser(['.csv'], async ({ read }) => {
      const body = await read()
      const [header, ...rows] = body.trim().split('\n').map(line => line.split(','))
      const data = rows.map(row => Object.fromEntries(header!.map((h, i) => [h, row[i]])))
      return { kind: 'document', data: { rows: data }, meta: {}, nodes: [] }
    })
  },
}))
```

### Register the parser

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

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

### Read the result

```ts [read.ts]
const team = await content.get('/teams/roster')
team?.data.rows   // parsed CSV rows, queryable and listable
```

::note
Return `data` with empty `nodes` for any data-only format (TOML, CSV, INI): entries stay queryable and listable but carry no rendered body.
::


## Sitemap

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