Skip to content

Relation Field ​

The Relation field type enables users to create relationships between entries in different collections within the CMS. There are two types of relations supported, depending on the target collection type:

The Content Editor comes with the Backlinks sidebar panel that shows all entries that reference the current entry via Relation fields. This makes it easy to see how entries are connected and navigate between them, for example, to see all blog posts that are tagged with a specific tag.

User Interface ​

Editor ​

Radio buttons (single select) or checkboxes (multi select) for choosing related entries from another collection. If there are many entries, a dropdown with search functionality will be used instead. Use the dropdown_threshold option to customize when to switch to the dropdown UI.

For multi-select options with many entries, a tag input UI will be used instead of checkboxes to save space. Items can be reordered by dragging and dropping or using right/left arrow keys. Items can also be removed by clicking the ✕ icon on each item.

The options are listed in the following order:

  • List items in a file, with the file option: the order of the items in the list.
  • Entries in an entry collection with the reorder option: the manual order of the entries. Entries without an order value, such as the ones just created with the Add button, come last.
  • Entries in a single-file collection: the order of the entries in the array.
  • Entries in any other entry collection: alphabetical order of the labels.

When the related collection is an entry collection, the field also offers an Add button labeled with the collection’s singular label, e.g. “Add Tag” or “Add Author”. It opens a dialog to create a related entry without leaving the entry being edited, so users don’t have to save their work, go to the other collection, create the entry there and come back — or pick a wrong entry just to be able to save.

The dialog is a single-pane editor with all the fields of the related collection. If the collection has multiple locales, a locale switcher in the dialog header lets users fill in each of them. Clicking Add validates the new entry the same way a save does; if a required field is empty, the dialog stays open and the error is shown on the field. Once added, the new entry is selected in the Relation field right away, listed among the options like any other entry, and shown by its label in the Preview Pane.

The new entry is not saved on its own. It’s kept with the draft and committed together with the entry being edited when the user saves, in a single commit, so the two never go out of sync: a blog post and the tags created for it land in the repository at the same time. Until then, the entry only exists in the draft:

  • If the user deselects the new entry before saving, it’s dropped rather than created for nothing.
  • If the user adds two entries with the same title, the second one gets a distinct slug, e.g. svelte-1, the same way it would if they were created one after another.
  • The dialog can be nested: a Relation field in the new entry has its own Add button, and the entries created there are saved along with everything else, as long as they are still referenced.
  • Files attached to the new entry, such as an author’s avatar, are uploaded in the same commit.
  • The pending entries are part of the auto-saved draft, so they survive a page reload along with the rest of the changes.

The button is not offered in the following cases:

  • The related collection is a file collection, with the file option. The button creates a new entry, but such a field selects an item from a list within an existing file. Adding an item would mean editing that file from another entry, which could conflict with changes made to the file elsewhere, so the item is added by editing the file itself instead.
  • The related collection has the create: false option.
  • The related collection has reached its limit, counting the entries pending in the draft. The button is then shown disabled.
  • The Relation field is read-only.
  • The entry being edited is saved through the Editorial Workflow, or the related collection is under the workflow on its own. A pull request stands for a single entry in the workflow, so an entry created on the fly would either be invisible until the pull request is published, or skip the review the related collection asks for. Create the related entry in its own collection instead.

Preview ​

A string or a list of strings representing the selected related entries, formatted according to the display_fields option.

Data Type ​

A string or an array of strings, depending on whether the multiple option is set to true or false. Each string represents the value of the related entry as defined by the value_field option.

In some cases, it can also be a number or an array of numbers if the value_field of the related collection is of a numeric type, like an ID.

If the required option is set to false and no related entries are selected, the value will be null for single select or an empty array for multi select.

Cascading Updates ​

Like a relational database that cascades an update of a referenced key, Sveltia CMS keeps Relation field values pointing at the right entry when the entry they reference is renamed. Renaming a related entry in the Slug panel rewrites every entry referencing it, in the same commit as the rename, so no references are left dangling.

This applies whenever the stored value is derived from the related entry’s identity, which covers the default {{slug}}, any template containing {{slug}}, such as {{locale}}/{{slug}}, and the canonical slug key. It does not apply to a value_field pointing at an ordinary content field, such as {{title}}, because such a value doesn’t change when the entry is renamed — but it does break if somebody edits that field. It’s one more reason to prefer the default {{slug}}, as noted under value_field.

Cascading Deletions ​

Deletions are cascaded in the same way. When an entry is deleted, whether from the Content Editor or by selecting one or more entries in the entry list, every entry referencing it through a Relation field is rewritten in the same commit as the deletion: a single-select field is cleared, and the deleted entry is dropped from a multi-select field’s list. The confirmation dialog tells the user how many entries will be updated. Unlike a rename, this applies whatever the value_field is, because references are matched on the stored value rather than derived from the slug. With the Editorial Workflow, the updates go into the same pull request as the deletion.

A reference is never removed at the cost of the referencing entry’s validity, though. If clearing it would break the field’s own validation rules — a required field left with nothing selected, or a multi-select field left with fewer than min items — the deletion is refused, and the dialog lists the entries and fields standing in the way so that the user can update them first. The check covers the whole selection: deleting two entries at once may be refused where deleting either on its own would go through. The Backlinks sidebar panel shows what references an entry, so users can see what a deletion would touch before they start.

Data Validation ​

  • If the required option is set to true, at least one related entry must be selected.
  • If the multiple option is enabled, the number of selected entries must be between the min and max limits, if specified.
  • If the pattern option is provided, the stored value of the selected entry, as defined by the value_field option, must match the regular expression. For multi select, the stored values joined with commas, e.g. foo,bar,baz, must match instead: as with Decap CMS, the pattern is tested against the whole selection rather than against each value, so use ^[a-z-]+(,[a-z-]+)*$ rather than ^[a-z-]+$ to accept lowercase slugs only. Numbers are tested as strings, and the pattern is not tested while nothing is selected.

Options ​

In addition to the common field options, the Relation field supports the following options:

Required Options ​

widget ​

  • Type: string
  • Default: string

Must be set to relation.

collection ​

  • Type: string
  • Default: undefined

The name of the collection to relate to. This collection must exist in the CMS configuration, and can be either an entry collection or a file collection. Use _singletons to relate to a singleton. If the target collection is a file collection or the singleton collection, the file option must also be specified.

Optional Options ​

Breaking changes from Netlify/Decap CMS

Sveltia CMS does not support the deprecated camelCase valueField, displayFields and searchFields options. Use value_field, display_fields and search_fields instead.

The options_length option is also not supported in Sveltia CMS because the performance has been improved significantly.

file ​

  • Type: string
  • Default: undefined

The name of a file within the target file collection, or of a singleton, to relate to. Required if the target collection is a file collection or the singleton collection.

value_field ​

  • Type: string
  • Default: {{slug}}

The field from the related collection to use as the value for the relation. This field’s value will be stored in the entry using the Relation field. It can be one of the following:

  • {{slug}}: Use the slug of the related entry.
  • A field name from the related collection, e.g., id or title.
  • A template string that references fields in the related collection using the syntax {{field_name}}. For example, {{fields.id}} or {{fields.title}}.
  • translationKey, or any other key configured with the i18n.canonical_slug.key option: Use the canonical slug of the related entry, which is shared across locales. See below.

The {{locale}} template tag can be used to include the current locale in the value field, e.g. {{locale}}/{{slug}}, which is useful for i18n support.

In a nested collection, an entry’s slug is its path below the collection folder, so {{slug}} resolves to something like company/about. Where every entry is stored as an index file, the shared file name is left out of that path, exactly as it is in a preview path: an entry stored at content/pages/company/about/_index.md is referenced as company/about, not company/about/_index. The collection’s own index file is the exception, keeping its name so that a reference to it isn’t empty.

In a single-file collection, an entry’s slug is its position in the array, which changes when the entries are reordered or one is deleted. The value field must therefore refer to a field, preferably one with a unique value like an ID; {{slug}}, including the default, is reported as invalid.

When using template strings, keep the following in mind:

  • A field named slug must be prefixed with fields. like {{fields.slug}} to avoid ambiguity with the special {{slug}} variable.
  • Nested fields can also be referenced using dot notation, e.g., {{author.name}}.
  • To reference list items, use a wildcard * for the index, e.g., {{tags.*}} or {{gallery.*.image}}. This works for a list field with the field or fields option.

A plain field name or dot-notation path without the curly brackets, such as title, name.first or cities.*.id, is treated as if it were enclosed in {{…}}. The same applies to display_fields and search_fields. The exception is a bare slug, which refers to the field named slug ({{fields.slug}}), not the entry slug; use {{slug}} for the latter.

The value field must be unique across all entries in the related collection to avoid conflicts. For example, using {{title}} as the value field is not recommended unless you can guarantee that all titles are unique. That’s why the default is {{slug}}, which is unique by design.

display_fields ​

  • Type: array of strings
  • Default: ["title"] if value_field is {{slug}}, otherwise the value of value_field option

The fields from the related collection to display in the Relation field UI when selecting related entries. This should be an array of field names. The values of these fields will be concatenated and shown as the label for each related entry.

String templates can be used to customize the display format. For example, to show both first and last names from separate fields, you can use either of the following:

yaml
display_fields: ['{{first_name}} {{last_name}}']
toml
display_fields = ["{{first_name}} {{last_name}}"]
json
{
  "display_fields": ["{{first_name}} {{last_name}}"]
}
js
{
  display_fields: ["{{first_name}} {{last_name}}"],
}
yaml
display_fields: ['first_name', 'last_name']
toml
display_fields = ["first_name", "last_name"]
json
{
  "display_fields": ["first_name", "last_name"]
}
js
{
  display_fields: ["first_name", "last_name"],
}

search_fields ​

  • Type: array of strings
  • Default: value of display_fields option

The fields from the related collection to search against when filtering related entries in the Relation field UI. This should be an array of field names, which can also be string templates, just like display_fields. By default, it uses the same fields as specified in the display_fields option.

default ​

  • Type: string, number, array of strings, or array of numbers
  • Default: null or []

The default value for the field. Should be a string or number for single select, or an array of strings or numbers for multi select, depending on the multiple option. An array with multiple off, or a single value with multiple on, is reported as a config validation error on the login screen.

  • Type: integer
  • Default: 5

The number of related entries at which to switch from radio buttons/checkboxes to a dropdown with search functionality. If the number of entries in the target collection is greater than this threshold, a dropdown will be used.

multiple ​

  • Type: boolean
  • Default: false

Whether to allow selecting multiple related entries.

min ​

  • Type: integer
  • Default: 0

The minimum number of related entries required. This enables validation to ensure that users select at least this many entries. Ignored if multiple is set to false.

max ​

  • Type: integer
  • Default: Infinity

The maximum number of related entries allowed. This enables validation to prevent users from selecting more than this many entries. Ignored if multiple is set to false.

filters ​

  • Type: array of filter objects
  • Default: []

An array of filter objects to limit the related entries shown in the Relation field UI. Each filter object has the following properties:

  • field: The field name in the related collection to filter on. Use slug to filter by entry slug or fields.fieldName to filter by a content field named fieldName (the fields. prefix is required to disambiguate from the entry slug when the field is literally named slug). A slug filter matches the same form the value takes, so in a nested collection it’s the entry’s path without the shared index file name. If the field holds multiple values, such as a Select field with multiple: true or a List field without subfields, an entry matches when any of its values is included in values.

  • values: An array of strings or numbers representing the values to match. A value may be one of the following template tags, which are resolved from the current entry being edited. The tag has to be the whole value, not part of a longer string like tag-{{slug}}:

    • {{slug}}: Resolved to the current entry's slug.
    • {{fields.fieldName}}: Resolved to the value of a field named fieldName in the current entry. If the field holds multiple values, the template is expanded to all of them, so an entry matches when it shares any of them with the current entry.

    Unresolvable templates (e.g. {{slug}} for a new, unsaved entry, or {{fields.fieldName}} for a field with no values) are ignored, causing the filter to be skipped.

  • exclude (optional): If true, entries matching the filter are excluded instead of included. An entry whose field holds multiple values is excluded when any of them matches. Default: false.

Example — show only published entries in a specific category:

yaml
filters:
  - field: draft
    values: [false]
  - field: category
    values: ['news', 'updates']
toml
[[filters]]
field = "draft"
values = [false]

[[filters]]
field = "category"
values = ["news", "updates"]
json
{
  "filters": [
    {
      "field": "draft",
      "values": [false]
    },
    {
      "field": "category",
      "values": ["news", "updates"]
    }
  ]
}
js
{
  filters: [
    {
      field: 'draft',
      values: [false],
    },
    {
      field: 'category',
      values: ['news', 'updates'],
    },
  ],
}

Example — exclude the current entry from a “Related Articles” Relation field (self-exclusion):

yaml
filters:
  - field: slug
    values: ['{{slug}}']
    exclude: true
toml
[[filters]]
field = "slug"
values = ["{{slug}}"]
exclude = true
json
{
  "filters": [
    {
      "field": "slug",
      "values": ["{{slug}}"],
      "exclude": true
    }
  ]
}
js
{
  filters: [
    {
      field: 'slug',
      values: ['{{slug}}'],
      exclude: true,
    },
  ],
}

Examples ​

Selecting Entries from an Entry Collection ​

Assuming you have the following entry collection named categories:

yaml
collections:
  - name: categories
    label: Categories
    folder: content/categories
    fields:
      - name: title
        label: Title
        widget: string
      - name: slug
        label: Slug
        widget: string
      - name: description
        label: Description
        widget: text
toml
[[collections]]
name = "categories"
label = "Categories"
folder = "content/categories"
[[collections.fields]]
name = "title"
label = "Title"
widget = "string"
[[collections.fields]]
name = "slug"
label = "Slug"
widget = "string"
[[collections.fields]]
name = "description"
label = "Description"
widget = "text"
json
{
  "collections": [
    {
      "name": "categories",
      "label": "Categories",
      "folder": "content/categories",
      "fields": [
        {
          "name": "title",
          "label": "Title",
          "widget": "string"
        },
        {
          "name": "slug",
          "label": "Slug",
          "widget": "string"
        },
        {
          "name": "description",
          "label": "Description",
          "widget": "text"
        }
      ]
    }
  ]
}
js
{
  collections: [
    {
      name: 'categories',
      label: 'Categories',
      folder: 'content/categories',
      fields: [
        {
          name: 'title',
          label: 'Title',
          widget: 'string',
        },
        {
          name: 'slug',
          label: 'Slug',
          widget: 'string',
        },
        {
          name: 'description',
          label: 'Description',
          widget: 'text',
        },
      ],
    },
  ],
}

You can create a Relation field in another collection to select a single category:

yaml
fields:
  - name: category
    label: Category
    widget: relation
    collection: categories
    value_field: slug
    display_fields: [title]
    search_fields: [title, description]
toml
[[fields]]
name = "category"
label = "Category"
widget = "relation"
collection = "categories"
value_field = "slug"
display_fields = ["title"]
search_fields = ["title", "description"]
json
{
  "fields": [
    {
      "name": "category",
      "label": "Category",
      "widget": "relation",
      "collection": "categories",
      "value_field": "slug",
      "display_fields": ["title"],
      "search_fields": ["title", "description"]
    }
  ]
}
js
{
  fields: [
    {
      name: 'category',
      label: 'Category',
      widget: 'relation',
      collection: 'categories',
      value_field: 'slug',
      display_fields: ['title'],
      search_fields: ['title', 'description'],
    },
  ],
}

Output example when the selected category has a slug of news:

yaml
category: news
toml
category = "news"
json
{
  "category": "news"
}

Referencing a File in a File Collection, Multiple Select ​

Assuming you have the following cities file in a data file collection:

yaml
collections:
  - name: data
    label: Data
    files:
      - name: locations
        label: Locations
        file: data/locations.yaml
        fields:
          - name: cities
            label: Cities
            widget: list
            fields:
              - name: name
                label: Name
                widget: string
              - name: country
                label: Country
                widget: string
toml
[[collections]]
name = "data"
label = "Data"
[[collections.files]]
name = "locations"
label = "Locations"
file = "data/locations.yaml"
[[collections.files.fields]]
name = "cities"
label = "Cities"
widget = "list"
[[collections.files.fields.fields]]
name = "name"
label = "Name"
widget = "string"
[[collections.files.fields.fields]]
name = "country"
label = "Country"
widget = "string"
json
{
  "collections": [
    {
      "name": "data",
      "label": "Data",
      "files": [
        {
          "name": "locations",
          "label": "Locations",
          "file": "data/locations.yaml",
          "fields": [
            {
              "name": "cities",
              "label": "Cities",
              "widget": "list",
              "fields": [
                {
                  "name": "name",
                  "label": "Name",
                  "widget": "string"
                },
                {
                  "name": "country",
                  "label": "Country",
                  "widget": "string"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
js
{
  collections: [
    {
      name: 'data',
      label: 'Data',
      files: [
        {
          name: 'locations',
          label: 'Locations',
          file: 'data/locations.yaml',
          fields: [
            {
              name: 'cities',
              label: 'Cities',
              widget: 'list',
              fields: [
                {
                  name: 'name',
                  label: 'Name',
                  widget: 'string',
                },
                {
                  name: 'country',
                  label: 'Country',
                  widget: 'string',
                },
              ],
            },
          ],
        },
      ],
    },
  ],
}

You can create a Relation field in another collection to select multiple cities from the locations file:

yaml
fields:
  - name: favorite_cities
    label: Favorite Cities
    widget: relation
    collection: data
    file: locations
    multiple: true
    min: 1
    max: 3
    value_field: '{{cities.*.name}}'
    display_fields: ['{{cities.*.name}}, {{cities.*.country}}']
    search_fields: ['{{cities.*.name}}']
toml
[[fields]]
name = "favorite_cities"
label = "Favorite Cities"
widget = "relation"
collection = "data"
file = "locations"
multiple = true
min = 1
max = 3
value_field = "{{cities.*.name}}"
display_fields = ["{{cities.*.name}}, {{cities.*.country}}"]
search_fields = ["{{cities.*.name}}"]
json
{
  "fields": [
    {
      "name": "favorite_cities",
      "label": "Favorite Cities",
      "widget": "relation",
      "collection": "data",
      "file": "locations",
      "multiple": true,
      "min": 1,
      "max": 3,
      "value_field": "{{cities.*.name}}",
      "display_fields": ["{{cities.*.name}}, {{cities.*.country}}"],
      "search_fields": ["{{cities.*.name}}"]
    }
  ]
}
js
{
  fields: [
    {
      name: 'favorite_cities',
      label: 'Favorite Cities',
      widget: 'relation',
      collection: 'data',
      file: 'locations',
      multiple: true,
      min: 1,
      max: 3,
      value_field: '{{cities.*.name}}',
      display_fields: ['{{cities.*.name}}, {{cities.*.country}}'],
      search_fields: ['{{cities.*.name}}'],
    },
  ],
}

Note that a wildcard (*) is used in the value_field, display_fields, and search_fields options to reference list items within the cities field.

Output example when the selected favorite cities are “San Francisco”, “Tokyo”, and “Paris”:

yaml
favorite_cities:
  - San Francisco
  - Tokyo
  - Paris
toml
favorite_cities = ["San Francisco", "Tokyo", "Paris"]
json
{
  "favorite_cities": ["San Francisco", "Tokyo", "Paris"]
}

Referencing Entries Across Locales ​

When entry slugs are localized, each localized entry stores the default locale’s slug in an extra translationKey property. Unlike {{slug}}, that property holds the same value in every locale, so it can be used as the value field to reference an entry regardless of the locale being edited:

yaml
fields:
  - name: parent
    label: Parent Page
    widget: relation
    i18n: true
    collection: pages
    value_field: translationKey
    display_fields: [title]
    search_fields: [title]
toml
[[fields]]
name = "parent"
label = "Parent Page"
widget = "relation"
i18n = true
collection = "pages"
value_field = "translationKey"
display_fields = ["title"]
search_fields = ["title"]
json
{
  "fields": [
    {
      "name": "parent",
      "label": "Parent Page",
      "widget": "relation",
      "i18n": true,
      "collection": "pages",
      "value_field": "translationKey",
      "display_fields": ["title"],
      "search_fields": ["title"]
    }
  ]
}
js
{
  fields: [
    {
      name: 'parent',
      label: 'Parent Page',
      widget: 'relation',
      i18n: true,
      collection: 'pages',
      value_field: 'translationKey',
      display_fields: ['title'],
      search_fields: ['title'],
    },
  ],
}

The translationKey property is not defined as a field, but it’s still a valid value field. If you have renamed the property with the i18n.canonical_slug.key option, such as ref for Jekyll, use that key instead.