Content Model
You define content as typed documents. A collection holds many of them, a global holds one, and fields make up each document. Relations link documents across collections. All of it lives in content.config.ts, and the runtime enforces what you define.
Collections
A collection is a set of documents that share the same schema. Define one with defineCollection. The key is the identifier used in URLs (/api/:key) and in relations. The labels are human-facing names.
defineCollection({ key: "posts", labels: { singular: "Post", plural: "Posts" }, fields: [ { key: "title", type: "text", required: true }, { key: "slug", type: "slug", required: true }, { key: "body", type: "markdown" }, ],});Every collection document gets an auto-generated id and createdAt/updatedAt timestamps. You add the rest. There is no separate slug field config or timestamp toggle: add a slug field or a date field if your content needs one.
A collection can carry an admin block (titleField, defaultSort) used by future admin tooling, and a hooks map for lifecycle behavior. See Configuration for the full options.
Globals
A global is a singleton document. Use it for site-wide settings, navigation, or anything that exists exactly once.
defineGlobal({ key: "siteSettings", label: "Site Settings", fields: [{ key: "siteName", type: "text" }],});Globals expose GET /api/globals/:key and PUT /api/globals/:key. They support the same field types and hooks as collections.
Fields
Fields are the building blocks of every document. Each field has a key, a type, and type-specific options. The FieldBase properties available on every field:
| Property | Type | Purpose |
|---|---|---|
key | string | The field identifier in the document |
label | string | Human-facing name |
description | string | Help text for editors |
required | boolean | Reject saves when the value is empty |
hidden | boolean | Hide from editors but keep in schema |
searchable | boolean | Include in text search |
indexed | boolean | Create a database index |
localized | boolean | Store per-locale variants |
defaultValue | unknown | Value used when none is provided |
validate | function | Custom validation |
access | { read?, update? } | Per-field access control |
See Field Types for every built-in type and its options.
Relations
A relation links one document to another. Use the relation field type with to set to the target collection key, and many to control cardinality.
defineCollection({ key: "posts", fields: [ { key: "tags", type: "relation", to: "tags", many: true }, ],});Relations resolve lazily. When querying, pass include to expand related documents in a single request, and depth to control how far expansion goes:
curl "http://localhost:3000/api/posts?include=tags&depth=1"The response replaces each relation field with the full related document(s) instead of a bare reference.
Groups and arrays
Two structural field types let you nest:
group: a fixed set of nested fields, stored as a single object. Use it for composite values like an address or a social link.array: a repeatable nested field. Use it for lists of structured items. Theofproperty takes a full field definition (not just a type string), so each array item can itself be a group.
{ key: "socialLinks", type: "array", of: { key: "socialLink", type: "group", fields: [ { key: "label", type: "text", required: true }, { key: "url", type: "text", required: true }, ], }}Tabs, rows, and collapsibles
Three field types exist for editor layout, not data shape:
tabs: split fields across named tabs. Each tab has its ownfieldsarray.row: place fields side by side on one row.collapsible: wrap fields in a collapsible panel with alabel.
These are structural only. They do not change how data is stored or queried.
What timbl does not model
timbl owns content structure, validation, and persistence. It does not own your frontend, routing, or presentation. A collection defines what a post is, not how it renders. That separation is what makes the same content portable across an Astro site, a Next.js app, and a mobile client. See Architecture for the package boundaries.
See also
- Configuration: the full schema builder API
- Field Types: every built-in field type
- HTTP API Reference: how collections, globals, and relations are queried
- Architecture: package map and dependency direction