Single-File Collections
An entry collection normally stores each entry in a file of its own. With the file option instead of folder, all the entries are stored in one JSON file, as an array of objects. This suits sites that read their content from a data file, such as a vanilla JavaScript site fetching a list of team members, products or links, or a static site generator’s data file.
TIP
Sveltia CMS can also edit such a file as a single entry, with a top-level List field in a file collection. Use that instead if the list is short and edited as a whole, e.g. a navigation menu, or if the file isn’t in JSON format.
Creating a Single-File Collection
Here is an example configuration for a list of team members:
collections:
- name: members
label: Team Members
label_singular: Team Member
file: /data/members.json
identifier_field: name
fields:
- { name: id, label: ID }
- { name: name, label: Name }
- { name: role, label: Role, required: false }
- { name: photo, label: Photo, widget: image, required: false }[[collections]]
name = "members"
label = "Team Members"
label_singular = "Team Member"
file = "/data/members.json"
identifier_field = "name"
[[collections.fields]]
name = "id"
label = "ID"
[[collections.fields]]
name = "name"
label = "Name"
[[collections.fields]]
name = "role"
label = "Role"
required = false
[[collections.fields]]
name = "photo"
label = "Photo"
widget = "image"
required = false{
"collections": [
{
"name": "members",
"label": "Team Members",
"label_singular": "Team Member",
"file": "/data/members.json",
"identifier_field": "name",
"fields": [
{ "name": "id", "label": "ID" },
{ "name": "name", "label": "Name" },
{ "name": "role", "label": "Role", "required": false },
{ "name": "photo", "label": "Photo", "widget": "image", "required": false }
]
}
]
}{
collections: [
{
name: "members",
label: "Team Members",
label_singular: "Team Member",
file: "/data/members.json",
identifier_field: "name",
fields: [
{ name: "id", label: "ID" },
{ name: "name", label: "Name" },
{ name: "role", label: "Role", required: false },
{ name: "photo", label: "Photo", widget: "image", required: false },
],
},
],
}The file then looks like this, with one object for each entry:
[
{ "id": "alice", "name": "Alice", "role": "Chair" },
{ "id": "bob", "name": "Bob", "role": "Treasurer" }
]The file option is a path to a .json file, relative to the repository’s root directory. JSON is the only supported format, so the format option can only be json, if set. The folder and file options can’t be used together.
The file doesn’t have to exist yet: it’s created when the first entry is saved.
How It Works
- Each object in the array is an entry. The entries are listed in the order of the array.
- A new entry is added to the end of the array.
- Deleting an entry removes its object from the array.
- The entries can always be reordered with the drag-and-drop UI, without the
reorderoption. The objects are moved within the array, and noorderfield is written. - Saving an entry rewrites the whole file, but only the object of that entry changes. Items that aren’t objects, and properties that aren’t defined as fields, are kept as they are.
Other entry collection options, such as create, delete, duplicate, limit, filter, summary, sortable_fields, view_filters, view_groups and media_folder, work as usual. A relative media_folder is relative to the folder of the file. As all the entries share that folder and can use any image in it, images are not deleted along with an entry.
Unsupported Options
The following entry collection options assume one file per entry, so they can’t be used with the file option, and the configuration is reported as invalid if they are:
extension,path,slugandslug_lengthnestedandmetaindex_filereorder, as the entries can always be reordered
Editorial Workflow is not supported either. The collection can’t use the editorial_workflow publish mode, and Open Authoring can’t be enabled. If Editorial Workflow is enabled for the whole site, set the collection’s publish_mode option to simple. For the same reason, a Relation field in the collection can’t refer to a collection that uses Editorial Workflow, as renaming or deleting an entry there would update the references to it in a pull request.
Internationalization
With i18n enabled for the collection, each object holds all the translations, like a file with the single_file structure does, whatever structure is configured for the site:
[
{
"en": { "name": "Alice", "role": "Chair" },
"fr": { "name": "Alice", "role": "Direction" }
}
]If the single_file_default_root structure is configured, the default locale’s fields are stored at the top level of each object instead. The {{locale}} placeholder can’t be used in the file path.
Identifying Entries
An entry is identified by its position in the array, which serves as its slug: 0 for the first entry, 1 for the second, and so on. The position changes when the entries are reordered or one is deleted, so the slug can’t be used to refer to an entry from elsewhere:
- A Relation field referring to the collection must store a field value with the
value_fieldoption, preferably a field with a unique value likeidin the example above. The default{{slug}}value is reported as invalid. - The
preview_pathandthumbnailoptions can’t contain the{{slug}}tag. Use a field instead, e.g./team/{{id}}. - The URL of an entry in the CMS points to its position, so a bookmarked entry may open another one after a reorder.
- Unsaved changes are not backed up in the browser, as a backup could otherwise be restored to another entry.
The commit author and date of an entry are those of the file’s last commit, which may be about another entry, so the entries can’t be sorted by them.
Editing at the Same Time
Risk of data loss
All the entries are stored in one file, and Sveltia CMS doesn’t lock entries while someone edits them. Keep the following in mind when several people edit the same collection.
Before saving, Sveltia CMS checks the repository for changes made by someone else, then applies only the user’s change to the file as it is now. A colleague’s change to another entry is kept.
The save is refused if the entry being edited has been changed or deleted in the meantime, or another entry has moved to its position because entries were added, deleted or reordered. The user is then asked to cancel editing and open the entry again from the list to make the edits. Unlike an entry stored in a file of its own, the user can’t save over the other change, as the entry at the same position may be a different one. Deleting or reordering entries is refused the same way.
There is still a short window between the check and the commit itself:
- With GitHub, the commit is rejected if someone else has committed in between, and the user can save again.
- With Gitea/Forgejo, the commit is rejected if the file has changed in between.
- With GitLab, the other commit is overwritten, and the change made in it is lost. If several people edit a large file frequently, consider taking turns, or split the content into several collections.
As the file changes with every save, its commit history covers all the entries, and the History panel in the Content Editor’s sidebar lists every commit made to the file.