---
title: Content-type schema
description: The YAML keys that define a content-type, the block dropped into a page section and rendered by one element template.
updated: 2026-06-22
next: page-types
---

A content-type defines a reusable **content block**: a typed set of fields plus the element template that renders one instance. Blocks are dropped into a [page-type](/en/docs/page-types) section and rendered by [`_contents`](/en/docs/templating-philosophy). Field definitions live exclusively inside `tabsets`, never at the type root.

## Top-level keys

| Key | Type | Notes |
|---|---|---|
| `name` | string | Backoffice display label. Live: `name: En-tête de section`. |
| `template` | string | Full element template path. Live: `'elements/entete.liquid'`. `.liquid` is the only loadable extension; you may also drop it (`'elements/entete'`) and the engine appends it. Any other extension is a 500, not a fallback. |
| `icon` | string | Font Awesome Pro id (`heading`, `star`, `newspaper`, `envelope`, `quote-left`). |
| `group` | string | Backoffice grouping label for the block selector. Live: `'Accueil'`, `'La revue'`, `'Bibliothèque de blocs'`. |
| `tabsets` | object | `tabsets.<key>.{name, fields}`. All fields live here. |

## Fields and tabsets

Every field belongs to a tabset. A tabset is an editing tab in the backoffice; `tabsets.<key>.name` is its label, `tabsets.<key>.fields` its field map. A content-type may declare several tabsets, for example one per pipeline stage in a form-handler block (`main`, `validation`, `actions`).

Each field key is a camelCase identifier and maps to a `{type, label, …}` descriptor. See [Custom fields](/en/docs/custom-fields) for the field-type catalogue and per-type config.

```yaml
mediaText:
  name: Media + text
  template: 'elements/mediaText.liquid'
  icon: file
  group: 'Block library'
  tabsets:
    main:
      name: Content
      fields:
        kicker:
          type: text
          label: Kicker
        title:
          type: text
          label: Title
        text:
          type: wysiwyg
          label: Body
        ctaLabel:
          type: text
          label: Link label
        ctaPage:
          type: page
          label: Internal link
        ctaUrl:
          type: text
          label: External URL
          translatable: false
        image:
          type: image
          label: Image
          config:
            versions:
              wide:
                label: Wide
                width: 1200
                height: 900
              full:
                label: Full width
                width: 1920
                height: 1080
    config:
      name: Configuration
      fields:
        layout:
          type: select
          label: Layout
          translatable: false
          default: imageRight
          config:
            options:
              imageRight: Image right
              imageLeft: Image left
              imageFull: Image full width
              textOnly: Text only
```

<div class="q-callout q-callout-note"><i class="fa-solid fa-circle-info lead"></i>
  <div><b>Field identifiers stay verbatim.</b> Keys like <code>title</code>, <code>text</code> and <code>category</code> are real content identifiers from the live site and read back as <code>content.custom.title</code> in templates. They are data, not UI labels, and do not get translated — renaming one is a schema change <i>and</i> a data migration, since the stored values are keyed by it.</div></div>

## Backoffice grouping

Two independent mechanisms organise the block selector:

| Mechanism | Where | Effect |
|---|---|---|
| `# Section: <Name>` | a level-0 YAML comment above a run of content-types | groups every following content-type under that heading |
| `group: '<Name>'` | a property on the content-type | tags the single content-type with a selector group |

Convention reuses the same label for both: a `group:` value matches the `# Section:` comment that precedes it.

## The `<<: *custom-fields` anchor

Reference skills prescribe a `config` tabset carrying `<<: *custom-fields`, a YAML anchor that injects shared display options (`removeSpaceBottom`, `showDivider`) into every content-type.

<div class="q-callout q-callout-warn"><i class="fa-solid fa-triangle-exclamation lead"></i>
  <div><b>Recommended, not enforced.</b> The <code>&lt;&lt;: *custom-fields</code> anchor is a best-practice convention, not an engine requirement. The live fil-rouge site does not use it: its <code>config</code> tabsets hold ordinary fields (a display-option <code>select</code>) instead of the anchor. Only reference a YAML anchor you have actually defined, an absent anchor (<code>*custom-fields</code>, <code>*_robots</code>) breaks the push.</div></div>

## Form-handler shape

A content-type can carry the full form pipeline as separate tabsets: input fields in `main`, a `validate` field in a `validation` tabset, a `save` field in an `actions` tabset. The `save` field's `config.key` names the target collection.

```yaml
newsletterForm:
  template: 'elements/newsletterForm.liquid'
  tabsets:
    main:
      fields:
        title:
          type: text
          label: Title
        intro:
          type: wysiwyg
          label: Intro
    validation:
      name: Validation
      fields:
        validation:
          type: validate
          config:
            rules:
              email: [email, required]
              consent: [required]
    actions:
      name: Actions
      fields:
        saveToCollection:
          type: save
          config:
            key: abonnes        # target collection
```

See [Custom fields](/en/docs/custom-fields) for the `validate` and `save` field config, and [Template variables](/en/docs/template-variables) for `content.custom` and `content.form.<field>.errors`.

## Related

<div class="q-cards">
  <a class="q-card reco" href="/en/docs/custom-fields"><span class="k">Recommended</span><span class="t">Custom fields</span><span class="d">The field-type catalogue and per-type config.</span></a>
  <a class="q-card" href="/en/docs/page-types"><span class="t">Page-type schema</span><span class="d">The sections that hold content blocks.</span></a>
  <a class="q-card" href="/en/docs/content-model"><span class="t">The content model</span><span class="d">Content-type vs page-type vs collection.</span></a>
</div>
