Collections
Collections are defined in src/cms/collections/ and registered in src/cms/cms.config.ts. Each collection becomes a database table, a set of TypeScript types, Zod validators, and an admin UI.
Basic Collection
Section titled “Basic Collection”import { defineCollection, fields } from "../core/define";
export default defineCollection({ slug: "posts", labels: { singular: "Post", plural: "Posts" }, timestamps: true, drafts: true, versions: { max: 20 }, fields: { title: fields.text({ required: true }), slug: fields.slug({ from: "title", unique: true, admin: { position: "sidebar" } }), body: fields.richText(), author: fields.relation({ collection: "authors", admin: { position: "sidebar" } }), },});Register it in src/cms/cms.config.ts:
import { defineConfig } from "./core/define";import posts from "./collections/posts";
export default defineConfig({ database: { dialect: "sqlite" }, collections: [posts],});Collection Options
Section titled “Collection Options”| Option | Type | Description |
|---|---|---|
slug | string | URL-safe identifier, used as table name prefix |
labels | { singular, plural } | Display names in admin |
singleton | boolean | Single document (e.g., front page) |
timestamps | boolean | Auto _createdAt / _updatedAt |
drafts | boolean | Enable draft/published status |
versions | { max: number } | Keep version snapshots |
pathPrefix | string | URL prefix for public pages (e.g., "blog") |
preview | boolean | string | Enable preview link (string for static URL) |
labelField | string | Field used as document display name |
views | object | List column config (see Admin UI) |
auth | boolean | Enable login/session handling |
Label Field
Section titled “Label Field”By default, the admin uses the title field as the document display name (in relation selects, DataTable, breadcrumbs). If your collection doesn’t have a title field, or you want a different field, set labelField:
defineCollection({ slug: "authors", labels: { singular: "Author", plural: "Authors" }, labelField: "name", fields: { name: fields.text({ required: true }), title: fields.text(), // work title, not the display name },});Fallback chain: labelField → field named title → field named name → first text field.
Singletons
Section titled “Singletons”defineCollection({ slug: "front-page", labels: { singular: "Front Page", plural: "Front Page" }, singleton: true, fields: { ... },})Singletons show under “Singles” in the sidebar. One document per collection.
Internationalization
Section titled “Internationalization”Enable locales in cms.config.ts and mark fields as translatable:
export default defineConfig({ locales: { default: "en", supported: ["en", "fi"], }, collections: [posts],});fields: { title: fields.text({ translatable: true }), body: fields.richText({ translatable: true }), category: fields.select({ options: ["tech", "design"] }), // not translated}Translatable fields get a separate _translations table. Non-translatable fields stay on the main table. The admin shows a language switcher on edit pages.