BackmatterPricingDocs
Contents

Fields

Every kind on the field export, and what it stores.

field is the frontmatter a model stores. Each call is one key on defineModel or defineObject. @backmatter/core exports it. @backmatter/next re-exports the same object.

models/blog.ts
import { defineModel, field } from "@backmatter/core";

export default defineModel({
  type: "blog",
  path: "content/blog/{slug}.mdx",
  route: "/blog/{slug}",
  fields: {
    title: field.text({ required: true }),
    cover: field.image(),
    views: field.number(),
    featured: field.boolean(),
    authors: field.relation("people"),
  },
});

{ required: true } is optional on every kind. Leave it off and the key may be absent. A required string must contain something other than spaces. 0 and false count as set. A YAML key with no value is missing. A key the model does not declare is kept.

CallValue in the file
field.text()string
field.image()string, a URL
field.media()string, a catalog image URL
field.number()number
field.boolean()boolean
field.relation("people")string, the other document's slug
field.blocks()array of { id, type, props }
field.object(link)one object
field.array(link)array of that object

where matches slug and the scalar kinds: text, number, and boolean. A relation is loaded with include. Querying is those calls. How a Next.js page mounts the document is Next.js.

Text, number, boolean

---
title: Hello
views: 12
featured: true
---

Use extra folders in path when the repo groups files by site or language, for example content/en/page/{slug}.json. That is ordinary path text, not document identity.

Image

field.image() stores a URL.

cover: https://example.com/cover.jpg

An object written with src, url, or href is saved as that string.

Media

field.media() stores the original CDN URL of an image in the workspace catalog.

cover: https://cdn.backmatter.app/img/med_018f6b3e-7c2a-7b1d-8e4f-9a0b1c2d3e4f

A transform URL and an image hosted somewhere else are not valid. The options on a public URL are Media. field.image() remains any URL.

Relation

The argument is the other model's type. The value is that document's slug.

authors: field.relation("people")
authors: mads

include loads content/people/mads.mdx one level deep. A missing file is null.

Blocks

field.blocks() stores a list. Each item has an id, a type, and props. type is a component in blocks/. A required blocks field needs at least one item.

blocks: field.blocks({ required: true })
[{ "id": "1", "type": "Hero", "props": { "heading": "Hello" } }]

A model with format: "page" keeps its body in this field. An MDX file keeps its body in the file. Components in blocks/ are the tags in that body.

Object and array

A reusable group of fields is defineObject in objects/. The file name is the object name, and the name is an identifier such as link.

objects/link.ts
import { defineObject, field } from "@backmatter/core";

export default defineObject({
  name: "link",
  fields: {
    label: field.text({ required: true }),
    href: field.text({ required: true }),
  },
});
models/navbar.ts
import { defineModel, field } from "@backmatter/core";
import link from "../objects/link";

export default defineModel({
  type: "navbar",
  singleton: true,
  format: "page",
  path: "content/navbar.json",
  fields: {
    items: field.array(link),
  },
});
items:
  - label: Docs
    href: /docs

field.object(link) stores one of those objects. field.array("link") refers to the object by name, which is how an object can contain itself. Nested fields use the same kinds, including required.