Skip to content

Entry Views ​

Sveltia CMS provides several options to customize how entries are displayed in the listing view. Users can sort, group and filter entries based on specific fields to improve navigation and organization.

Table View ​

The entry list can be shown in three views: a table, a list and a grid of thumbnails.

  • The table view shows a column for each piece of information that tells the entries apart, such as the date, the author and the category.
  • The list view only shows the entry summary.
  • The grid view is only available when the entries have thumbnails.

The table view is the default, until a user picks another view with the buttons in the toolbar. A collection opened before the table view was introduced keeps the view it was shown in then, usually the list view, until the user picks the table.

The table view is shown on a large screen, where the columns fit. On a smaller screen, the list view is shown instead. In reorder mode, the entries are always shown in a list.

The table is read-only: click a row to open the entry, as in the list view. To change the values of many entries at once, use bulk editing.

Choosing Columns ​

By default, the table shows the entry summary, followed by a column for each field the entries can be sorted by, as they are the fields that tell the entries apart. The identifier field, usually title, is left out, as the summary already shows it.

You can choose the columns with the table_columns option instead. It accepts a list of field names, which can include:

  • nested fields in dot notation like author.name
  • the special key _summary for the entry summary
  • the special keys slug, commit_date and commit_author

To give a column a header other than the field’s label, use an object with the field and label properties:

yaml
collections:
  - name: posts
    label: Blog posts
    folder: /content/posts
    fields:
      - { name: title, label: Title }
      - { name: date, label: Published Date, widget: datetime }
      - { name: author, label: Author, widget: object, fields: [{ name: name, label: Name }] }
      - { name: draft, label: Draft, widget: boolean }
      - { name: body, label: Body, widget: richtext }
    table_columns:
      - title
      - date
      - { field: author.name, label: Author }
      - draft
toml
[[collections]]
name = "posts"
label = "Blog posts"
folder = "/content/posts"
table_columns = [
  "title",
  "date",
  { field = "author.name", label = "Author" },
  "draft",
]

[[collections.fields]]
name = "title"
label = "Title"

[[collections.fields]]
name = "date"
label = "Published Date"
widget = "datetime"

[[collections.fields]]
name = "author"
label = "Author"
widget = "object"
fields = [{ name = "name", label = "Name" }]

[[collections.fields]]
name = "draft"
label = "Draft"
widget = "boolean"

[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
json
{
  "collections": [
    {
      "name": "posts",
      "label": "Blog posts",
      "folder": "/content/posts",
      "fields": [
        { "name": "title", "label": "Title" },
        { "name": "date", "label": "Published Date", "widget": "datetime" },
        { "name": "author", "label": "Author", "widget": "object", "fields": [{ "name": "name", "label": "Name" }] },
        { "name": "draft", "label": "Draft", "widget": "boolean" },
        { "name": "body", "label": "Body", "widget": "richtext" }
      ],
      "table_columns": [
        "title",
        "date",
        { "field": "author.name", "label": "Author" },
        "draft"
      ]
    }
  ]
}
js
{
  collections: [
    {
      name: "posts",
      label: "Blog posts",
      folder: "/content/posts",
      fields: [
        { name: "title", label: "Title" },
        { name: "date", label: "Published Date", widget: "datetime" },
        { name: "author", label: "Author", widget: "object", fields: [{ name: "name", label: "Name" }] },
        { name: "draft", label: "Draft", widget: "boolean" },
        { name: "body", label: "Body", widget: "richtext" },
      ],
      table_columns: [
        "title",
        "date",
        { field: "author.name", label: "Author" },
        "draft",
      ],
    },
  ],
}

A column can also show a template, written the same way as the summary option, with the same tags and transformations. This lets the table show entry metadata such as {{locales}} for the enabled locales, {{dirname}}, {{filename}} and {{extension}}, or combine several values in one column:

yaml
table_columns:
  - title
  - '{{locales}}'
  - { field: '{{filename}}.{{extension}}', label: File }
  - { field: "{{date | date('YYYY-MM-DD')}}", label: Published }

A template made of a single tag, such as {{locales}} or {{commit_date}}, gets a header named after it, and its column can be sorted if the tag is one of the sortable fields. Give a longer template a label, or its header shows the template itself.

Each cell shows the field’s value the way a summary template would:

  • a date in the user’s format
  • a Select field’s option by its label
  • a Relation field’s referenced entry by its display value
  • a list as comma-separated values
  • a rich text field without its formatting

A Boolean field is shown as a check mark when it’s on. With i18n enabled, the values are taken from the default locale. If a listed field isn’t defined in the collection, Sveltia CMS raises a configuration error on startup.

A column whose field is also one of the sortable fields has a clickable header that sorts the entries by it, the same way the Sort menu does; click it again to reverse the order.

Static CMS compatibility

The summary_fields option of Static CMS, a list of field names where summary stands for the entry summary, is supported as an alias of table_columns. It’s ignored if table_columns is also defined.

Sorting ​

By default, Sveltia CMS supports sorting by title, name, date, author and description fields if they exist in the collection, as well as by the identifier field if it’s set with the identifier_field option. If the date and author fields are not present, Sveltia CMS will look for commit date and author information from Git history (if available) to enable sorting by those fields.

When the summary option is defined for a collection, Sveltia CMS also enables sorting by entry summaries. This allows you to sort entries based on the customized summary content, which can include multiple fields and string transformations.

When the reorder option is enabled for a collection, Sveltia CMS enables manual sorting of entries in the listing view. This allows users to drag and drop entries to reorder them as needed.

You can customize the sortable fields using the sortable_fields option. It accepts an array of field names that you want to enable for sorting in the entry listing view. It also accepts the following special keys, which read entry metadata instead of a field:

  • slug: Sort entries by their slugs.
  • commit_date: Sort entries by the date of the last commit that modified the entry file.
  • commit_author: Sort entries by the author of the last commit that modified the entry file.

The commit_date and commit_author keys are only shown when Git commit information is available for the collection’s entries; otherwise, they are ignored. Even with sortable_fields defined, Sveltia CMS adds them automatically when the information is available and the list doesn’t include date or author, respectively.

Entries without a value for the sort field, such as an empty date, are always listed last, whether the order is ascending or descending and whatever the field type.

The example below shows how to enable sorting by custom fields such as category and nested fields like author.name:

yaml
collections:
  - name: posts
    label: Blog posts
    folder: /content/posts
    fields:
      - { name: title, label: Title }
      - { name: published_date, label: Published Date, widget: datetime }
      - {
          name: author,
          label: Author,
          widget: object,
          fields: [{ name: name, label: Name }, { name: email, label: Email }],
        }
      - { name: category, label: Category }
      - { name: body, label: Body, widget: richtext }
    sortable_fields: [title, published_date, author.name, category]
toml
[[collections]]
name = "posts"
label = "Blog posts"
folder = "/content/posts"
sortable_fields = ["title", "published_date", "author.name", "category"]

[[collections.fields]]
name = "title"
label = "Title"

[[collections.fields]]
name = "published_date"
label = "Published Date"
widget = "datetime"

[[collections.fields]]
name = "author"
label = "Author"
widget = "object"

[[collections.fields.fields]]
name = "name"
label = "Name"

[[collections.fields.fields]]
name = "email"
label = "Email"

[[collections.fields]]
name = "category"
label = "Category"

[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
json
{
  "collections": [
    {
      "name": "posts",
      "label": "Blog posts",
      "folder": "/content/posts",
      "fields": [
        { "name": "title", "label": "Title" },
        { "name": "published_date", "label": "Published Date", "widget": "datetime" },
        {
          "name": "author",
          "label": "Author",
          "widget": "object",
          "fields": [
            { "name": "name", "label": "Name" },
            { "name": "email", "label": "Email" }
          ]
        },
        { "name": "category", "label": "Category" },
        { "name": "body", "label": "Body", "widget": "richtext" }
      ],
      "sortable_fields": ["title", "published_date", "author.name", "category"]
    }
  ]
}
js
{
  collections: [
    {
      name: "posts",
      label: "Blog posts",
      folder: "/content/posts",
      fields: [
        { name: "title", label: "Title" },
        { name: "published_date", label: "Published Date", widget: "datetime" },
        { name: "author", label: "Author", widget: "object", fields: [
          { name: "name", label: "Name" },
          { name: "email", label: "Email" },
        ] },
        { name: "category", label: "Category" },
        { name: "body", label: "Body", widget: "richtext" },
      ],
      sortable_fields: ["title", "published_date", "author.name", "category"],
    },
  ],
}

Each item in the sortable_fields list can also be an object with the following options, as in Decap CMS. Objects and field names can be mixed in the same list.

  • field: The field name or special key to sort by. Required.
  • label: The text shown in the Sort menu. Defaults to the field’s label.
  • default_sort: Makes the field the default sort key. Use asc or true for ascending order, or desc for descending order.

The example below sorts the entries by published_date in descending order by default, and shows the field as “Publish date” in the Sort menu:

yaml
sortable_fields:
  - title
  - { field: published_date, label: Publish date, default_sort: desc }
  - author.name
  - category

Only one item can have the default_sort option. Giving it to more than one item is reported as a config validation error on the login screen. The default sort applies until the user picks another sort key or order in the Sort menu, which Sveltia CMS remembers for the collection.

Extended syntax

Sveltia CMS supports an extended syntax used in Static CMS to define a default sort field and direction. This is useful if you want entries to be sorted by a date field in descending order by default. Here is the same default sort using the extended syntax:

yaml
sortable_fields:
  fields: [title, published_date, author.name, category]
  default:
    field: published_date
    direction: descending

The default direction is ascending if not specified.

For backward compatibility with Static CMS, the direction option accepts title case values: Ascending and Descending. However, None is not supported and has the same effect as ascending.

Known issue

Git commit information is currently loaded only with the GitHub backend. With the GitLab and Gitea/Forgejo backends, it’s only available for entries saved during the current session, due to API limitations. It’s not available in the local workflow or with the Test backend.

Grouping ​

The view_groups option allows you to group entries in the listing view based on specific field values. This is useful for organizing entries into categories or sections for easier navigation.

The example below demonstrates how to group blog posts by their draft status and by the year extracted from the date field:

yaml
collections:
  - name: posts
    label: Blog posts
    folder: /content/posts
    fields:
      - { name: title, label: Title }
      - { name: date, label: Published Date, widget: datetime }
      - { name: draft, label: Draft, widget: boolean }
      - { name: body, label: Body, widget: richtext }
    view_groups:
      - label: Drafts
        field: draft
      - label: Year
        field: date
        pattern: '\d{4}'
toml
[[collections]]
name = "posts"
label = "Blog posts"
folder = "/content/posts"

[[collections.fields]]
name = "title"
label = "Title"

[[collections.fields]]
name = "date"
label = "Published Date"
widget = "datetime"

[[collections.fields]]
name = "draft"
label = "Draft"
widget = "boolean"

[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"

[[collections.view_groups]]
label = "Drafts"
field = "draft"

[[collections.view_groups]]
label = "Year"
field = "date"
pattern = "\\d{4}"
json
{
  "collections": [
    {
      "name": "posts",
      "label": "Blog posts",
      "folder": "/content/posts",
      "fields": [
        { "name": "title", "label": "Title" },
        { "name": "date", "label": "Published Date", "widget": "datetime" },
        { "name": "draft", "label": "Draft", "widget": "boolean" },
        { "name": "body", "label": "Body", "widget": "richtext" }
      ],
      "view_groups": [
        {
          "label": "Drafts",
          "field": "draft"
        },
        {
          "label": "Year",
          "field": "date",
          "pattern": "\\d{4}"
        }
      ]
    }
  ]
}
js
{
  collections: [
    {
      name: "posts",
      label: "Blog posts",
      folder: "/content/posts",
      fields: [
        { name: "title", label: "Title" },
        { name: "date", label: "Published Date", widget: "datetime" },
        { name: "draft", label: "Draft", widget: "boolean" },
        { name: "body", label: "Body", widget: "richtext" },
      ],
      view_groups: [
        {
          label: "Drafts",
          field: "draft",
        },
        {
          label: "Year",
          field: "date",
          pattern: "\\d{4}",
        },
      ],
    },
  ],
}

Without a pattern or a comparison option, entries are grouped by the field value itself, as with the draft group above. With a pattern, they are grouped by the part of the field value that matches it, such as the four-digit year of the date field.

Extended syntax

Sveltia CMS supports an extended syntax used in Static CMS to define a default group. Here is the same configuration using the extended syntax:

yaml
view_groups:
  groups:
    - name: drafts
      label: Drafts
      field: draft
    - name: year
      label: Year
      field: date
      pattern: '\d{4}'
  default: year

In this syntax, each group needs a name, so that the default option can refer to it. The default option has to be the name of one of the groups. Any other name is reported as a config validation error on the login screen, because the collection would otherwise open ungrouped.

To sort the Year group in descending order by date, you can add the sortable_fields property as described in the Sorting section above:

yaml
sortable_fields:
  fields: [date, title]
  default:
    field: date
    direction: descending

A named view group can also be used with the reorder option to let editors reorder entries within their own group.

Grouping by a Comparison ​

A view group can also split the entries by a comparison, using the same options as a view filter. The entries satisfying the condition are grouped under the group’s label, and the other entries under “Other”. For example, the following group lists the upcoming events first, followed by the past and undated ones:

yaml
view_groups:
  - label: Upcoming
    field: date
    gte: '{{today}}'

Filtering ​

The view_filters option allows you to define preset filters that editors can quickly apply to the entry listing view. This is useful for quickly accessing specific subsets of entries based on common criteria.

yaml
collections:
  - name: posts
    label: Blog posts
    folder: /content/posts
    fields:
      - { name: title, label: Title }
      - { name: date, label: Published Date, widget: datetime }
      - { name: draft, label: Draft, widget: boolean }
      - { name: category, label: Category }
      - { name: body, label: Body, widget: richtext }
    view_filters:
      - label: Drafts
        field: draft
        pattern: true
      - label: Posts from 2024
        field: date
        pattern: '^2024'
      - label: Travel or Food
        field: category
        pattern: travel|food
toml
[[collections]]
name = "posts"
label = "Blog posts"
folder = "/content/posts"

[[collections.fields]]
name = "title"
label = "Title"

[[collections.fields]]
name = "date"
label = "Published Date"
widget = "datetime"

[[collections.fields]]
name = "draft"
label = "Draft"
widget = "boolean"

[[collections.fields]]
name = "category"
label = "Category"

[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"

[[collections.view_filters]]
label = "Drafts"
field = "draft"
pattern = "true"

[[collections.view_filters]]
label = "Posts from 2024"
field = "date"
pattern = "^2024"

[[collections.view_filters]]
label = "Travel or Food"
field = "category"
pattern = "travel|food"
json
{
  "collections": [
    {
      "name": "posts",
      "label": "Blog posts",
      "folder": "/content/posts",
      "fields": [
        { "name": "title", "label": "Title" },
        { "name": "date", "label": "Published Date", "widget": "datetime" },
        { "name": "draft", "label": "Draft", "widget": "boolean" },
        { "name": "category", "label": "Category" },
        { "name": "body", "label": "Body", "widget": "richtext" }
      ],
      "view_filters": [
        {
          "label": "Drafts",
          "field": "draft",
          "pattern": "true"
        },
        {
          "label": "Posts from 2024",
          "field": "date",
          "pattern": "^2024"
        },
        {
          "label": "Travel or Food",
          "field": "category",
          "pattern": "travel|food"
        }
      ]
    }
  ]
}
js
{
  collections: [
    {
      name: "posts",
      label: "Blog posts",
      folder: "/content/posts",
      fields: [
        { name: "title", label: "Title" },
        { name: "date", label: "Published Date", widget: "datetime" },
        { name: "draft", label: "Draft", widget: "boolean" },
        { name: "category", label: "Category" },
        { name: "body", label: "Body", widget: "richtext" },
      ],
      view_filters: [
        {
          label: "Drafts",
          field: "draft",
          pattern: "true",
        },
        {
          label: "Posts from 2024",
          field: "date",
          pattern: "^2024",
        },
        {
          label: "Travel or Food",
          field: "category",
          pattern: "travel|food",
        },
      ],
    },
  ],
}

Extended syntax

Sveltia CMS supports an extended syntax used in Static CMS to define a default filter. Here is the same configuration using the extended syntax:

yaml
view_filters:
  filters:
    - name: drafts
      label: Drafts
      field: draft
      pattern: true
    - name: posts_2024
      label: Posts from 2024
      field: date
      pattern: '^2024'
    - name: travel_or_food
      label: Travel or Food
      field: category
      pattern: travel|food
  default: drafts

In this syntax, each filter needs a name, so that the default option can refer to it. The default option has to be the name of one of the filters. Any other name is reported as a config validation error on the login screen, because the collection would otherwise open unfiltered.

Comparing Values ​

A view filter can compare the field value with a given value instead of, or in addition to, matching a pattern. This makes it possible to filter entries by a date, such as upcoming and past events, or by a number, such as products above a certain price. The following options are available:

OptionDescription
eqThe field value has to be equal to the given value.
neThe field value has to be different from the given value.
ltThe field value has to be less than the given value.
lteThe field value has to be less than or equal to the given value.
gtThe field value has to be greater than the given value.
gteThe field value has to be greater than or equal to the given value.
inThe field value has to be equal to one of the given values, defined as an array.
not_inThe field value has to be different from all the given values, defined as an array.
emptyThe field value has to be empty (true) or not (false).

The value of a DateTime field is compared as a date, so the given value has to be in the same format as the field value, or one of the following template tags:

TagDescription
{{now}}The current date and time.
{{today}}The current date in the YYYY-MM-DD format. Use this rather than {{now}} with a date-only field, so that an entry dated today is included in a gte comparison.
{{year}}, {{month}}, {{day}}, {{hour}}, {{minute}}, {{second}}The parts of the current date and time, which can also be used in a pattern, e.g. ^{{year}} for the entries of this year.

The tags are resolved in the user’s local timezone whenever the entry list is updated, and every minute while such a filter or group is applied, so a filter like “Upcoming events” keeps working without any change to the configuration.

A number field value and a numeric given value are compared as numbers. Any other value is compared as a string. An entry without a value for the field only matches ne, not_in and empty: true. When several options are defined for one filter, all of them have to be satisfied.

The empty option tells the entries that have a value for the field from those that don’t. A value is empty if:

  • it’s null, an empty string or an empty list
  • it’s an Object field whose subfields are all empty
  • the field is missing from the entry altogether, for example because the field was added to the configuration after the entry was created

Use this rather than eq or ne, which can’t express a missing value in TOML, where there is no null:

yaml
view_filters:
  - label: With author
    field: author
    empty: false
  - label: Without author
    field: author
    empty: true
toml
[[collections.view_filters]]
label = "With author"
field = "author"
empty = false

[[collections.view_filters]]
label = "Without author"
field = "author"
empty = true
json
{
  "view_filters": [
    { "label": "With author", "field": "author", "empty": false },
    { "label": "Without author", "field": "author", "empty": true }
  ]
}
js
{
  view_filters: [
    { label: "With author", field: "author", empty: false },
    { label: "Without author", field: "author", empty: true },
  ],
}

The comparison options can also be used with the extended syntax described above, for example to apply the “Upcoming” filter by default:

yaml
view_filters:
  filters:
    - name: upcoming
      label: Upcoming
      field: date
      gte: '{{today}}'
    - name: past
      label: Past
      field: date
      lt: '{{today}}'
  default: upcoming

The example below defines the filters for a list of events:

yaml
collections:
  - name: events
    label: Events
    folder: /content/events
    fields:
      - { name: title, label: Title }
      - { name: date, label: Date, widget: datetime, time_format: false }
      - { name: capacity, label: Capacity, widget: number }
      - { name: status, label: Status, widget: select, options: [scheduled, canceled] }
    view_filters:
      - label: Upcoming
        field: date
        gte: '{{today}}'
      - label: Past
        field: date
        lt: '{{today}}'
      - label: This year
        field: date
        pattern: '^{{year}}'
      - label: Large venues
        field: capacity
        gte: 100
      - label: Not canceled
        field: status
        ne: canceled
toml
[[collections]]
name = "events"
label = "Events"
folder = "/content/events"

[[collections.fields]]
name = "title"
label = "Title"

[[collections.fields]]
name = "date"
label = "Date"
widget = "datetime"
time_format = false

[[collections.fields]]
name = "capacity"
label = "Capacity"
widget = "number"

[[collections.fields]]
name = "status"
label = "Status"
widget = "select"
options = ["scheduled", "canceled"]

[[collections.view_filters]]
label = "Upcoming"
field = "date"
gte = "{{today}}"

[[collections.view_filters]]
label = "Past"
field = "date"
lt = "{{today}}"

[[collections.view_filters]]
label = "This year"
field = "date"
pattern = "^{{year}}"

[[collections.view_filters]]
label = "Large venues"
field = "capacity"
gte = 100

[[collections.view_filters]]
label = "Not canceled"
field = "status"
ne = "canceled"
json
{
  "collections": [
    {
      "name": "events",
      "label": "Events",
      "folder": "/content/events",
      "fields": [
        { "name": "title", "label": "Title" },
        { "name": "date", "label": "Date", "widget": "datetime", "time_format": false },
        { "name": "capacity", "label": "Capacity", "widget": "number" },
        { "name": "status", "label": "Status", "widget": "select", "options": ["scheduled", "canceled"] }
      ],
      "view_filters": [
        {
          "label": "Upcoming",
          "field": "date",
          "gte": "{{today}}"
        },
        {
          "label": "Past",
          "field": "date",
          "lt": "{{today}}"
        },
        {
          "label": "This year",
          "field": "date",
          "pattern": "^{{year}}"
        },
        {
          "label": "Large venues",
          "field": "capacity",
          "gte": 100
        },
        {
          "label": "Not canceled",
          "field": "status",
          "ne": "canceled"
        }
      ]
    }
  ]
}
js
{
  collections: [
    {
      name: "events",
      label: "Events",
      folder: "/content/events",
      fields: [
        { name: "title", label: "Title" },
        { name: "date", label: "Date", widget: "datetime", time_format: false },
        { name: "capacity", label: "Capacity", widget: "number" },
        { name: "status", label: "Status", widget: "select", options: ["scheduled", "canceled"] },
      ],
      view_filters: [
        {
          label: "Upcoming",
          field: "date",
          gte: "{{today}}",
        },
        {
          label: "Past",
          field: "date",
          lt: "{{today}}",
        },
        {
          label: "This year",
          field: "date",
          pattern: "^{{year}}",
        },
        {
          label: "Large venues",
          field: "capacity",
          gte: 100,
        },
        {
          label: "Not canceled",
          field: "status",
          ne: "canceled",
        },
      ],
    },
  ],
}