---
title: "Frontmatter and page data"
description: "What a page looks like once it's parsed, and where each part comes from."
canonical_url: "https://content.comark.dev/guide/frontmatter"
---
# Frontmatter and page data

> What a page looks like once it's parsed, and where each part comes from.

A Markdown file has two parts: a **frontmatter** block with metadata, and a **body** with the content. Comark Content parses both and hands you one object per page.

```md [content/blog/hello.md]
---
title: Hello, world
description: The first post on this blog.
date: 2026-03-01
tags: [news, launch]
---

# Hello, world

This is the **body** of the page.
```

```ts
const page = await content.get('/blog/hello')

page.path // '/blog/hello'
page.data // { title: 'Hello, world', description: '…', date: '2026-03-01', tags: ['news', 'launch'] }
page.nodes // the parsed body, a tree of nodes
page.meta // information about the file itself
```

## `data`: the frontmatter

The frontmatter is YAML between two `---` lines at the top of the file. Every field ends up in `page.data` with its YAML type: strings, numbers, booleans, dates as strings, lists, and nested objects.

There are no required fields. Two are worth adopting because other parts of Comark Content read them:

- `title` labels the page in [`navigation()`](https://content.comark.dev/guide/navigation) and in search results.
- `description` is carried into navigation items too, and it's a natural fit for `<meta name="description">`.

Add any field your app needs, such as `date`, `author`, `draft`, or `tags`. [`list()`](https://content.comark.dev/guide/read-and-list#list-pages) returns `data` for every page without parsing bodies, so frontmatter is the right place for anything you filter or sort on.

A file with no frontmatter is still a page; its `data` is an empty object.

## `nodes`: the body

`page.nodes` is the Markdown body parsed by [Comark](https://comark.dev) into a tree of nodes: headings, paragraphs, lists, code blocks, and any [components](https://comark.dev/syntax/components) you use. You don't walk this tree yourself. Pass it to a Comark renderer and it becomes HTML or framework components:

```vue
<MarkdownDocument :value="page" />
```

The [framework guides](https://content.comark.dev/getting-started/render-your-first-page) show the renderer for Vue, React, Svelte, and plain HTML. Bodies are parsed the first time you `get()` a page and kept in memory after that, so a second read of the same page skips the parse.

## `path` and `meta`: the file

`page.path` is the public path you read the page by; [Files and paths](https://content.comark.dev/guide/files-and-paths) explains how it's derived from the file's location.

`page.meta` describes the file rather than its content: its `kind` (`document` or `media`), MIME `type`, `extension`, and the [internal identifiers](https://content.comark.dev/guide/files-and-paths#internal-identifiers) `key`, `source`, and `stem`. Most apps never read `meta`; it's there when you debug or write plugins.

## Typing `data`

Until you generate types, `page.data` is a `Record<string, any>`: any field compiles, and none autocompletes. Run one command to infer types from your actual files:

```bash [Terminal]
npx comark-content prepare
```

After that, `page.data.title` is a `string`, `page.data.tags` is a `string[]`, and a typo in a field name is a compile error. [Add TypeScript types](https://content.comark.dev/guide/typescript) walks through it, including the Vite plugin that runs it for you.

## Validating frontmatter

Types help while you write code; they don't stop a contributor from committing `date: yesterday`. When you need a hard guarantee, declare a JSON Schema on the source and add the [`schemaValidation` plugin](https://content.comark.dev/plugins/built-in/schema-validation). Invalid files are reported and dropped, and the same schema drives the generated types.

## Pages without a body

With the [`json`](https://content.comark.dev/plugins/built-in/json) or [`yaml`](https://content.comark.dev/plugins/built-in/yaml) plugin, `.json` and `.yaml` files become pages whose whole content is `data`, with empty `nodes`. That suits settings, team lists, and other structured content that lives next to your Markdown. The [`markdownFields` plugin](https://content.comark.dev/plugins/built-in/markdown-fields) parses Markdown strings inside those files when you need rich text in a field.


## Sitemap

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