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.
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.
| Call | Value 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.
import { defineObject, field } from "@backmatter/core";
export default defineObject({
name: "link",
fields: {
label: field.text({ required: true }),
href: field.text({ required: true }),
},
});
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.