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:
- Entries in an entry collection
- List items in a specific file in a file collection
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
fileoption: the order of the items in the list. - Entries in an entry collection with the
reorderoption: 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.
Creating Related Entries
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
fileoption. 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: falseoption. - 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
requiredoption is set totrue, at least one related entry must be selected. - If the
multipleoption is enabled, the number of selected entries must be between theminandmaxlimits, if specified. - If the
patternoption is provided, the stored value of the selected entry, as defined by thevalue_fieldoption, 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.,
idortitle. - 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 thei18n.canonical_slug.keyoption: 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
slugmust be prefixed withfields.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 thefieldorfieldsoption.
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:
arrayofstrings - Default:
["title"]ifvalue_fieldis{{slug}}, otherwise the value ofvalue_fieldoption
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:
display_fields: ['{{first_name}} {{last_name}}']display_fields = ["{{first_name}} {{last_name}}"]{
"display_fields": ["{{first_name}} {{last_name}}"]
}{
display_fields: ["{{first_name}} {{last_name}}"],
}display_fields: ['first_name', 'last_name']display_fields = ["first_name", "last_name"]{
"display_fields": ["first_name", "last_name"]
}{
display_fields: ["first_name", "last_name"],
}search_fields
- Type:
arrayofstrings - Default: value of
display_fieldsoption
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, orarray of numbers - Default:
nullor[]
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.
dropdown_threshold
- 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:
arrayof 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. Useslugto filter by entry slug orfields.fieldNameto filter by a content field namedfieldName(thefields.prefix is required to disambiguate from the entry slug when the field is literally namedslug). Aslugfilter 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 withmultiple: trueor a List field without subfields, an entry matches when any of its values is included invalues.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 liketag-{{slug}}:{{slug}}: Resolved to the current entry's slug.{{fields.fieldName}}: Resolved to the value of a field namedfieldNamein 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): Iftrue, 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:
filters:
- field: draft
values: [false]
- field: category
values: ['news', 'updates'][[filters]]
field = "draft"
values = [false]
[[filters]]
field = "category"
values = ["news", "updates"]{
"filters": [
{
"field": "draft",
"values": [false]
},
{
"field": "category",
"values": ["news", "updates"]
}
]
}{
filters: [
{
field: 'draft',
values: [false],
},
{
field: 'category',
values: ['news', 'updates'],
},
],
}Example — exclude the current entry from a “Related Articles” Relation field (self-exclusion):
filters:
- field: slug
values: ['{{slug}}']
exclude: true[[filters]]
field = "slug"
values = ["{{slug}}"]
exclude = true{
"filters": [
{
"field": "slug",
"values": ["{{slug}}"],
"exclude": true
}
]
}{
filters: [
{
field: 'slug',
values: ['{{slug}}'],
exclude: true,
},
],
}Examples
Selecting Entries from an Entry Collection
Assuming you have the following entry collection named categories:
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[[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"{
"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"
}
]
}
]
}{
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:
fields:
- name: category
label: Category
widget: relation
collection: categories
value_field: slug
display_fields: [title]
search_fields: [title, description][[fields]]
name = "category"
label = "Category"
widget = "relation"
collection = "categories"
value_field = "slug"
display_fields = ["title"]
search_fields = ["title", "description"]{
"fields": [
{
"name": "category",
"label": "Category",
"widget": "relation",
"collection": "categories",
"value_field": "slug",
"display_fields": ["title"],
"search_fields": ["title", "description"]
}
]
}{
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:
category: newscategory = "news"{
"category": "news"
}Referencing a File in a File Collection, Multiple Select
Assuming you have the following cities file in a data file collection:
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[[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"{
"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"
}
]
}
]
}
]
}
]
}{
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:
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}}'][[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}}"]{
"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}}"]
}
]
}{
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”:
favorite_cities:
- San Francisco
- Tokyo
- Parisfavorite_cities = ["San Francisco", "Tokyo", "Paris"]{
"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:
fields:
- name: parent
label: Parent Page
widget: relation
i18n: true
collection: pages
value_field: translationKey
display_fields: [title]
search_fields: [title][[fields]]
name = "parent"
label = "Parent Page"
widget = "relation"
i18n = true
collection = "pages"
value_field = "translationKey"
display_fields = ["title"]
search_fields = ["title"]{
"fields": [
{
"name": "parent",
"label": "Parent Page",
"widget": "relation",
"i18n": true,
"collection": "pages",
"value_field": "translationKey",
"display_fields": ["title"],
"search_fields": ["title"]
}
]
}{
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.