---
title: "Choose a source"
description: "Where your Markdown lives, and how to switch from local files to a repository or a storage bucket."
canonical_url: "https://content.comark.dev/sources"
---
# Choose a source

> Where your Markdown lives, and how to switch from local files to a repository or a storage bucket.

A **source** is the one required option of a Content instance. It answers a single question: where are the files? Everything else, from `get()` to navigation and search, works the same whatever you answer.

Most projects start with files next to the app and never need anything else:

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

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

## Pick a source

::card-group{cols="2"}
  :::card{icon="i-lucide-folder" title="Filesystem" to="https://content.comark.dev/sources/filesystem"}
  A folder in your project. Fastest dev loop, with file watching. The default choice.
  :::

  :::card{icon="i-simple-icons-github" title="GitHub" to="https://content.comark.dev/sources/github"}
  A repository, branch, and folder. Content lives apart from the app and updates without a redeploy.
  :::

  :::card{icon="i-lucide-boxes" title="Key-value storage" to="https://content.comark.dev/sources/unstorage"}
  Any unstorage driver: S3, Cloudflare KV, Redis, HTTP, and more.
  :::

  :::card{icon="i-lucide-file-box" title="Snapshot" to="https://content.comark.dev/sources/snapshot"}
  Already-parsed content, for hosts with no filesystem and for browser apps.
  :::

  :::card{icon="i-lucide-wrench" title="Custom" to="https://content.comark.dev/sources/custom"}
  Implement three methods to read from a database, a CMS API, or any other backend.
  :::
::

## Switch sources

Changing where content comes from is a change to one line. Here the same instance moves from a local folder to a GitHub repository:

```ts [content.ts] {2,6-10}
import { comarkContent } from 'comark-content'
import github from 'comark-content/sources/github'

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

await content.get('/about') // reads content/about.md in acme/docs, same as before
```

Public paths, `list()`, `navigation()`, and rendering don't change. What does change is how edits reach a running app: a local folder can be [watched](https://content.comark.dev/guide/watch), while a remote source is refreshed on a schedule or from a webhook. [Keep remote content up to date](https://content.comark.dev/deployment/remote-content) covers that.

## Where the source runs

The filesystem source uses Node's file APIs, so it runs wherever your server has the files: your machine, a container, or a Node host that deploys the `content/` folder. Serverless functions and edge runtimes often don't ship your files, and browsers never have them. For those, read from [GitHub](https://content.comark.dev/sources/github) or a [storage driver](https://content.comark.dev/sources/unstorage), or ship a [snapshot](https://content.comark.dev/sources/snapshot) built from your files. [Choose how to deploy](https://content.comark.dev/deployment) compares the options.

## One source per instance

An instance reads one source. A source can hold as many folders as you like, so a single filesystem source covers `content/blog/`, `content/docs/`, and anything else under `content/`.

Create a second instance when two bodies of content need different settings: one from GitHub and one local, or one with search and one without. [Combine content sources](https://content.comark.dev/advanced/hub) shows how to serve both through one API.

## Common options

The built-in sources share a few options:

| Option    | Effect                                                                                                                                                                                                               |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prefix`  | Prepended to every public path, for example `/blog`. See [Files and paths](https://content.comark.dev/guide/files-and-paths#add-a-url-prefix).                                                                       |
| `exclude` | Globs of files to leave out of the index, such as `drafts/**`.                                                                                                                                                       |
| `schema`  | A JSON Schema for the frontmatter, used for [generated types](https://content.comark.dev/guide/typescript#declare-a-schema-instead) and [validation](https://content.comark.dev/plugins/built-in/schema-validation). |

Each source page lists its own options in full.


## Sitemap

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