---
title: "Quick start"
description: "Install Comark Content, write one Markdown file, and read its title from a script."
canonical_url: "https://content.comark.dev/getting-started/installation"
---
# Quick start

> Install Comark Content, write one Markdown file, and read its title from a script.

In this guide you install Comark Content, write one Markdown file, and read its title from a script. The whole thing is one folder, two TypeScript files, and one command.

You need [Node.js](https://nodejs.org) 22.18 or later, which runs TypeScript files directly, and a package manager. You don't need a framework yet: the [next page](https://content.comark.dev/getting-started/render-your-first-page) covers rendering in Nuxt, Next.js, SvelteKit, and others.

::steps{level="3"}
### Create a project and install the package

Create an empty folder with a `package.json` that enables ES modules, then add `comark-content`:

```json [package.json]
{
  "name": "my-content",
  "private": true,
  "type": "module"
}
```

  :::code-group
  ```bash [pnpm]
  pnpm add comark-content
  ```

  ```bash [npm]
  npm install comark-content
  ```

  ```bash [yarn]
  yarn add comark-content
  ```

  ```bash [bun]
  bun add comark-content
  ```
  :::

The package depends on [`comark`](https://comark.dev) itself, so you don't install the parser separately.

### Write a Markdown file

Create a `content/` folder and one file inside it:

```md [content/about.md]
---
title: About
---

# About us

We write documentation for a living.
```

The block between the `---` lines is **frontmatter**: YAML metadata about the page. Comark Content reads it into `page.data`, so `title` becomes `page.data.title`. Everything below the frontmatter is the page body.

You can name the folder anything. `content/` is a common choice, and the next step points at it.

### Create the Content instance

The Content instance is the object you read pages from. Create it once, in its own file, and point it at your folder:

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

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

`fs('./content')` is a **source**: it tells the instance where your files live. The path is relative to the directory you run the script from. Other sources read from [GitHub](https://content.comark.dev/sources/github) or a [storage driver](https://content.comark.dev/sources/unstorage); the rest of the API stays the same.

### Read the page

Write a script that reads `/about` and prints its title:

```ts [index.ts]
import { content } from './content.ts'

const page = await content.get('/about')

if (!page) {
  throw new Error('No page at /about')
}

console.log(page.path) // /about
console.log(page.data.title) // About
```

Run it from the project root:

```bash [Terminal]
node index.ts
```

You see:

```text
/about
About
```
::

## What just happened

- `content.get('/about')` read `content/about.md`. The path is the file's location inside the source folder, without the `.md` extension. `content/index.md` would be `/`, and `content/blog/hello.md` would be `/blog/hello`. See [Files and paths](https://content.comark.dev/guide/files-and-paths).
- `page.data` holds the frontmatter as a plain object. `page.data.title` is the `title` you wrote. TypeScript sees it as `Record<string, any>` for now; [generated types](https://content.comark.dev/guide/typescript) make it autocomplete later.
- `page.nodes` holds the parsed body as a tree of nodes. You don't read that tree yourself: a Comark renderer turns it into HTML or components for your framework. The [next page](https://content.comark.dev/getting-started/render-your-first-page) shows how.
- `get()` returns `null` when no file matches, which is why the script checks the result before using it.

The first read parses the file and keeps the result in memory. Read the same path again in the same process, and it comes from that cache. A new process, like re-running the script, reads the file again.

## Edit and re-run

Change the title in `content/about.md` to `About us` and run `node index.ts` again. The script prints the new title.

In a framework, the process keeps running while you edit, so the instance needs to know that a file changed. The [framework guides](https://content.comark.dev/getting-started/render-your-first-page) set that up, and [Watch content changes](https://content.comark.dev/guide/watch) explains the options.

## Next steps

::card-group{cols="2"}
  :::card{icon="i-lucide-monitor" title="Render your first page" to="https://content.comark.dev/getting-started/render-your-first-page"}
  Pick your framework and turn `page.nodes` into a rendered page.
  :::

  :::card{icon="i-lucide-folder-tree" title="Files and paths" to="https://content.comark.dev/guide/files-and-paths"}
  How files, folders, and numeric prefixes map to public paths.
  :::

  :::card{icon="i-lucide-list" title="Read and list pages" to="https://content.comark.dev/guide/read-and-list"}
  `get()` and `list()` with their inputs and outputs.
  :::

  :::card{icon="i-lucide-shield-check" title="Add TypeScript types" to="https://content.comark.dev/guide/typescript"}
  Generate types so `page.data.title` autocompletes from your frontmatter.
  :::
::


## Sitemap

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