Collection-type schema
The YAML keys that define a collection of structured, listable entries, including displays, filters, ordering and detail-route previews.
A collection is a set of structured, listable entries (Articles, Members, Events). In the configuration it is declared under item-set-types, and the MCP tools that read and write its entries carry itemset in their names, which is the one place the old object name still shows. Entries are queried into pages by the query field declared on a page-type. Field definitions live exclusively inside tabsets, never at the type root. A collection entry's data is written under a data: {} wrapper, not custom: {}.
Top-level keys
| Key | Type | Notes |
|---|---|---|
name |
string | Collection name (backoffice section). |
objectName |
string | Singular item label ("Add a …"). Live: objectName: Article. |
icon |
string | Font Awesome Pro id. |
tabsets |
object | tabsets.<key>.{name, fields}. All fields live here. |
displays |
array | {type, config} entries. type is datatable, calendar or kanban. |
filters |
array | field keys exposed as backoffice filters. Allowed types only: checkbox, page, select, color, number. |
sortable |
bool or array | true for global manual drag-drop, or an array of category field keys for per-category. Stored in system _order. |
retention |
string or object | ISO-8601 duration (P1Y6M), or {duration, basedOn} where basedOn is created_at, updated_at (default) or a date field key — see Retention. |
bulkEditGroups |
object | optional {group: label} labels for the bulk-edit groups declared on fields via config.bulkEditable — see Bulk edit. Each key must match a group used by at least one field. |
preview |
array | {label, route, params} entries; mandatory wherever a detail route exists. |
fieldsets |
object | visual field grouping. |
There is no root-level label, fields, options or translations. Deprecated: root cols: (now displays[].config.cols) and root display: (now displays).
articles:
name: Articles
objectName: Article
icon: file
tabsets:
main:
name: Article
fields:
title:
type: text
label: Title
translatable: true
searchable: true
slug:
type: slug
label: Slug
config:
linkedField: title
excerpt:
type: textarea
label: Standfirst
translatable: true
body:
type: wysiwyg
label: Body
translatable: true
category:
type: select
config:
options:
vivant: Vivant
terrains: Terrains
idees: Idées
entretiens: Entretiens
author:
type: text
label: Author
translatable: false
date:
type: date
label: Publication date
image:
type: image
config:
versions:
card:
label: Card
width: 800
height: 600
cover:
label: Cover
width: 1600
height: 900
readingTime:
type: number
label: Reading time (min)
translatable: false
featured:
type: checkbox
config:
checkedValue: oui
uncheckedValue: non
displays:
- type: datatable
config:
cols: [image, title, category, date]
order:
column: date
sort: desc
filters: [category, featured]
preview:
- label: View on the site
route: article
params:
article.slug: '{{slug}}'
title, category and featured are real content identifiers from the live site and read back unchanged in queries and templates. They are data, not UI labels — and note that category stores vivant while it displays "Vivant": a where clause matches the stored key, never the label.Displays
displays is an array of {type, config}; each entry is a backoffice list view. When several are defined, config.label is the switcher button label.
datatable
config key |
Notes |
|---|---|
cols |
array of 3 to 5 field keys. Width hints inline: pic (70px), year (15%). |
size |
default, sm or xs. |
order |
{column: <fieldKey>, sort: asc|desc}, the backoffice default sort only. |
label |
switcher button label when multiple displays exist. |
calendar
config key |
Notes |
|---|---|
labelField |
the field used as the event label. |
label |
switcher button label. |
kanban
A Trello-style board. The columns are the options of one select (or smartselect with
source: static) field of the type, in their YAML order; dragging a card to another column
updates that field on the item.
config key |
Notes |
|---|---|
columnsField |
required — key of the field whose options define the columns. Must be a select or a smartselect with source: static, and not multiple (a card belongs to a single column). |
card |
{title, text, image} field keys shown on each card — all optional, each falls back to the first field of the matching type (title: text; text: textarea/wysiwyg; image: image). |
order |
{column, sort}, the sort of the cards inside each column (default created_at desc). |
label |
switcher button label. |
displays:
- type: datatable
config:
cols: [image, title, category]
- type: kanban
config:
label: Board
columnsField: category
card:
title: title
text: excerpt
image: image
Items whose value is empty (or no longer matches an option) are grouped in a leading "Non classé" column; dragging a card into it clears the field.
datatable or calendar display is declared alongside it, and the kanban
cannot be used as the legacy scalar display:. Avoid placing it first in
displays: the first entry is the default view.Filters
filters is an array of field keys exposed as backoffice filters. Only checkbox, page, select, color and number fields are allowed.
Inline edit
A select (or smartselect with source: static) field declared with config.inlineEditable: true
becomes editable directly in the datatable cells of the listing: changing the value saves the
item immediately, without opening it. The kanban display relies on the same partial-update contract
when a card is dragged to another column. For editing several items at once, or fields of other
types, see Bulk edit below.
Bulk edit
Fields opt into bulk edit with config.bulkEditable. In the backoffice listing, the right-click
menu of a selection then shows one entry per group; each entry opens a modal scoped to that
group's fields and applies the same values to every selected item. Groups keep business actions
separate — a "schedule publication" modal edits its two dates and nothing else.
bulkEditable: true— the field forms its own implicit single-field group (menu entry labelled after the field).bulkEditable: <group>— a group name ([a-zA-Z0-9_-]+); all fields sharing it are edited in one modal. The menu label isbulkEditGroups.<group>when declared at the type root, otherwise the capitalised group name.
Accepted only on text, textarea, number, date, select, color, checkbox, switch
and smartselect with source: static. On any other type (including slug) the configuration is
rejected.
news:
bulkEditGroups:
planning: Schedule publication # label of the context-menu entry
tabsets:
main:
fields:
category:
type: select
config:
options: { … }
bulkEditable: true # own entry, own single-field modal
publishFrom:
type: date
config:
bulkEditable: planning # same modal as publishUntil
publishUntil:
type: date
config:
bulkEditable: planning
In the modal, each field has a "modify this field" toggle: an untouched field leaves the items' values alone, an activated empty field clears them — the two are never confused. Values are applied through one call for the whole selection, and each item's history records the edit.
Ordering, three independent axes
Do not confuse the three ordering mechanisms; they apply to different surfaces.
| Axis | Where | Effect |
|---|---|---|
displays[].config.order |
a display | the CMS list's default sort. |
sortable |
the type | manual drag-drop in the backoffice, persisted to system _order. |
runtime sort: |
a query (or items field) |
ordering on the public site at render time. |
Slugs and archiving
- Slug via
config.linkedField(notsource:), auto-generated from another field. One slug field per type maximum. - Archive via a
checkboxfield withconfig.{checkedValue, uncheckedValue}, the canonical soft-archive (notchoice). This is orthogonal to the systemstate(draft/active) controlled by the MCPupdate-itemset-itemstateparam.
Retention
retention deletes items automatically once they expire. Two shapes:
# Legacy: duration only — basedOn defaults to updated_at
retention: P6M
# Full: explicit basedOn — created_at, updated_at, or a date field of the type
retention:
duration: P0D
basedOn: displayUntil # a field of type `date` declared in the tabsets
The planned deletion date is basedOn + duration. With a custom date field, an item whose field is
empty never expires, and the basedOn day is inclusive: the item survives the whole day and
expires at its end (P0D = "display until this day included"). Expired items are
soft-deleted to the collection trash, then purged for good 30 days later.
As soon as the item is trashed (manual delete or retention), each image/file field applies its
config.onDelete rule to the media it references: keep (default) leaves the media alone,
cascade sends it to the media trash systematically, orphan sends it only when no other element
references it. Trashed media follow the media-trash lifecycle (quarantined file, CDN purge, hard
delete 30 days later). Restoring the item from its trash restores the media it dragged along, as long
as they have not been purged yet.
In the backoffice, upcoming deletions appear at the top of the dashboard and in the media library
("Suppressions prévues"), and a special retention column (the computed deletion date, sortable)
can be added to the collection cols.
Preview for detail routes
preview declares backoffice "view on site" links. Each entry names a route and supplies params, whose values are template-evaluated per item. A preview is mandatory whenever a detail route exists for the collection.
params key must mirror the route token exactly, dots included: route path /{{article.slug}} means flat key article.slug: '{{slug}}'. A nested map (article: then slug:) silently breaks the backoffice list.See Routing and URLs for the route definition and Custom fields for the query field that reads the collection.
Related
Last updated September 6, 2026.