Skip to content
Quilium Docs
Back to site
Reference

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}}'
Field identifiers stay verbatim. Keys like 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.

A kanban is never the only display. The configuration is rejected unless a 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 is bulkEditGroups.<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 (not source:), auto-generated from another field. One slug field per type maximum.
  • Archive via a checkbox field with config.{checkedValue, uncheckedValue}, the canonical soft-archive (not choice). This is orthogonal to the system state (draft/active) controlled by the MCP update-itemset-item state param.

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 keys are flat and verbatim. A 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.