Collections

Choose the collections, their order and their options

Info

Collections are added automatically, one per Hugolify module the project imports: sections and taxonomies. The pages, indexes, menus and config collections come with the admin itself.

The options of a collection go under admin.collections, keyed by its name: pages, posts, persons, posts_categories…

Options by CMS

New in v2

An option has one name, whatever the CMS. Hugolify writes it the way each CMS reads it, and leaves it out where the CMS has no equivalent.

OptionDecapSveltiaStaticNetlifyPages CMSCloudCannonTina
enable✓✓✓✓✓✓✓
create✓✓✓✓✓✓✓
fields✓✓✓✓✓✓✓
blocks✓✓✓✓✓✓✓
filter✓✓✓✓–––
exclude––––✓––
path✓✓✓✓✓✓✓
media_folder✓✓✓✓–––
slug✓✓✓✓–––
sortable_fields✓✓✓✓✓––
summary✓✓✓✓–✓–
summary_fields–✓✓–✓––
thumbnail–✓–––✓–
view_filters✓✓✓✓–––
view_groups✓✓✓✓–––
reorder–✓–––––
bulk_edit–✓–––––
icon–✓–––✓–
nested✓✓✓✓✓––
card_preview–––––✓–

Enable or disable collections

A collection shows as soon as its module is imported. enable: false hides it, the module staying in place:

/config/_default/params.yaml

admin:
  collections:
    indexes:
      enable: true
    pages:
      enable: true
    # e.g. set to false to disable posts even if you load hugolify-theme-posts
    posts:
      enable: false
    # …

Order and group collections

New in v2

Collections are listed in the order of collection_groups: the groups as they come, and the collections of each group as they are listed. '*' stands for every enabled collection that no group names, in alphabetical order.

/config/_default/params.yaml

admin:
  collection_groups:
    - heading: Content
      collections: [pages, indexes]
    - heading: News
      collections: [posts, posts_categories, posts_tags]
    - heading: Other
      collections: ['*']
    - heading: Settings
      collections: [menus, config]

Quote the '*': in YAML, a bare * starts an alias.

Left undefined, the order is pages and indexes, then every other collection in alphabetical order, then menus and config:

admin:
  collection_groups:
    - collections: [pages, indexes]
    - collections: ['*']
    - collections: [menus, config]

To list every collection in alphabetical order, as v1 did, keep a single group holding '*'.

  • Without '*', the collections that no group names come last, in a group of their own. A collection never drops out of the list: to hide one, disable it.
  • A disabled collection is skipped, an unknown name is skipped with a warning, and a collection listed twice keeps its first place.
CMSResult
Sveltia CMSOrder, and a divider between two groups
Decap CMS, Netlify CMS, Static CMSOrder
Pages CMS, TinaOrder
CloudCannonGroups with their heading, once collection_groups is set

The heading is read by CloudCannon only. It requires one on each group, so it keeps its own order until collection_groups is set. A Sveltia CMS divider is a plain line, without a label.

Enable or disable file creation

create: false removes the button that adds an entry, as for indexes, whose entries come with the modules:

/config/_default/params.yaml

admin:
  collections:
    pages:
      create: false

Pages CMS reads it as operations.create, CloudCannon as disable_add, Tina as ui.allowedActions.create.

Override fields available for a collection

Each collection ships with a list of fields. fields replaces it:

/config/_default/params.yaml

admin:
  collections:
    pages:
      fields:
        - draft
        - title_page
        - description
        - featured_image
        - body

The default list of each collection is in its file under collections/fields/. A field can take options next to its name, see Options of a select field.

See the fields of each collection in repository

Blocks of a collection

The blocks field of every collection offers the blocks of admin.blocks.enable. To offer a collection a list of its own, set blocks on the collection, and name the collection in its blocks field:

/config/_default/params.yaml

admin:
  collections:
    posts:
      blocks: [paragraph, figure, quote]
      fields:
        - draft
        - title_page
        - description
        - body
        - blocks: posts # reads the blocks of the posts collection

Left as a plain blocks, the field keeps offering every enabled block.

Add filter

filter lists only the entries whose field holds a value. This is how pages and indexes share the content folder: a page carries isPage: true, a section index isIndex: true, each written by a hidden field.

/config/_default/params.yaml

admin:
  collections:
    pages:
      filter:
        - field: isPage
          value: true

Pages CMS has no filter: it leaves files out by name instead, see exclude.

Add path

path sets where a new entry is saved, inside the folder of the collection. It defaults to {{slug}}/_index: each entry gets a folder of its own, a Hugo branch bundle, which can hold its images and, in a nested collection, its children. Set {{slug}} to save the entries as single files instead:

/config/_default/params.yaml

admin:
  collections:
    posts:
      path: "{{slug}}" # content/posts/my-post.md rather than content/posts/my-post/_index.md

Media folders of a collection

Decap, Sveltia, Static and Netlify CMS

The uploads of a collection go to the global folders. Give the collection folders of its own:

/config/_default/params.yaml

admin:
  collections:
    persons:
      media_folder: /assets/images/persons
      public_folder: /images/persons

Add slug

slug names a new entry, and so its file or its folder. It defaults to {{slug}}, the title written for a URL, or to the ID of the entry with config.id: true. It takes the fields of the entry and the date of creation:

/config/_default/params.yaml

admin:
  collections:
    posts:
      slug: "{{year}}-{{month}}-{{slug}}" # 2026-11-my-post

Add sortable fields

New in v2

The fields of the Sort menu, in order. It replaces the sortable string of v1.

/config/_default/params.yaml

admin:
  collections:
    posts:
      sortable_fields:
        - field: date
          default_sort: desc
        - title
        - commit_date
  • default_sort sorts the list by that field when it opens: asc (or true) or desc. One field takes it.
  • commit_date and commit_author sort by the last commit, in the CMS that read the Git history.
  • A label replaces the label of the field in the Sort menu. Decap CMS alone reads it.

Left undefined, each CMS offers its own defaults, such as the title and the date.

CMSResult
Decap CMSsortable_fields, with default_sort and label on the field
Sveltia CMSsortable_fields, as a { fields, default } object once a field sorts by default
Static CMSsortable_fields, as a { fields, default } object
Netlify CMSsortable_fields, field names alone
Pages CMSview.sort, and view.default for the field sorting by default, without commit_date and commit_author
CloudCannon, TinaNothing: CloudCannon keeps its own Sort menu

Add summary

summary is the text each entry shows in the list of the collection, the title by default. It takes the fields of the entry in braces:

/config/_default/params.yaml

admin:
  collections:
    pages:
      summary: "{{title}}"

CloudCannon shows it as the text of the card, the title standing in when it comes out empty. A field is written in its own way, {{title}} - {{date}} becoming {title} - {date}, and a transformation after a pipe is dropped, as the two CMS do not share their names.

Add summary fields

New in v2 Sveltia CMS, Static CMS and Pages CMS

The columns of the table view, in order. It replaces the view.fields of Pages CMS.

/config/_default/params.yaml

admin:
  collections:
    persons:
      summary_fields:
        - summary
        - lastname
        - field: contact.email
          label: Email
        - draft
  • summary stands for the entry summary, as in Static CMS.
  • A nested field takes dot notation.
  • A label replaces the header the column takes from its field. Sveltia CMS alone reads it.

Left undefined, Sveltia CMS shows the summary followed by the sortable fields, Static CMS the summary alone, Pages CMS every field.

CMSResult
Sveltia CMStable_columns, with summary written _summary
Static CMSsummary_fields, without labels
Pages CMSview.fields, with summary written title, without labels
Decap CMS, Netlify CMS, TinaNothing: no table view
CloudCannonNothing: see card preview

Name a field of the collection

Sveltia CMS rejects a config whose column names no field of the collection, and shows the error instead of the CMS. Static CMS leaves the column empty, and Pages CMS drops it.

Sveltia CMS also takes its special keys (slug, commit_date, commit_author) and templates such as "{{date | date('YYYY-MM-DD')}}" as columns.

Add thumbnail

New in v2 Sveltia CMS and CloudCannon

The image shown beside each entry of the list, and in the grid view of Sveltia CMS.

A collection with featured_image shows it without a setting: that field is an image object holding the file in src, which Sveltia CMS would not find on its own, as it only looks for an Image field at the root of the entry. Name another field, or none:

/config/_default/params.yaml

admin:
  collections:
    persons:
      thumbnail: image.src # the default with featured_image
    posts:
      thumbnail: false     # no image

A list tries its fields in turn and shows the first one filled, e.g. thumbnail: [image.src, logo.src].

Name an Image field of the collection

Sveltia CMS rejects a config whose thumbnail names no field of the collection, and shows the error instead of the CMS.

CMSResult
Sveltia CMSthumbnail
CloudCannonthe image of the card, preview.gallery.image. A card_preview setting it wins
Other CMSNothing: Decap, Netlify and Static CMS guess the image from the field names, Pages CMS shows an Image field as a column of summary_fields

Add view filters

view_filters adds filters above the list: each one shows only the entries whose field matches its pattern.

/config/_default/params.yaml

admin:
  collections:
    pages:
      view_filters:
        - label: 'Posts published in 2020'
          field: date
          pattern: '2020'
        - label: 'Drafts'
          field: draft
          pattern: true

A pattern keeps its type: true matches a boolean field, '2020' a text. Static CMS gets the filters in its own { filters } object, each named after its label.

Add view groups

view_groups offers to group the list by the value of a field, or by the part of it a pattern matches, such as the year of a date.

/config/_default/params.yaml

admin:
  collections:
    pages:
      view_groups:
        - label: 'Draft'
          field: draft
        - label: 'Year'
          field: date
          pattern: '\d{4}'

Static CMS gets the groups in its own { groups } object, each named after its label.

Add reorder

New in v2 Only available with Sveltia CMS

reorder: true lets editors order the entries by drag and drop. The order is saved in the weight of each entry, which Hugo sorts pages by.

/config/_default/params.yaml

admin:
  collections:
    pages:
      reorder: true

Add bulk edit

New in v2 Only available with Sveltia CMS

Edit the simple fields of many entries at once, in a table that works like a spreadsheet, and save them in a single commit: turn draft on for a batch of posts, fix titles. Filter the list first to edit some entries only.

It is on for every collection, with every simple top-level field as a column: draft, title, date, weight, slug, a select… Only Boolean, Color, DateTime, Number, Relation, Select and String fields can be edited in bulk.

Saved on the branch

Like reordering, a bulk edit is committed to the branch directly. So it is off by default with publish_mode: editorial_workflow or preview, where it would bypass the review.

Turn it off, or pick the columns, for every collection or for one. Fields are named as in the front matter:

/config/_default/params.yaml

admin:
  bulk_edit: false               # every collection
  collections:
    posts:
      bulk_edit: true            # back on for the posts
    persons:
      bulk_edit: [title, draft]  # these columns, in this order

Collection icon

New in v2 Sveltia CMS and CloudCannon

The icon shown beside the collection in the sidebar. It is declared per icon set, so the same collection works whichever set the CMS uses, see Icon sets.

/config/_default/params.yaml

admin:
  collections:
    pages:
      icon:
        bootstrap_icons: file-earmark
        iconoir: page
        lucide: file
        material_icons: description
        material_symbols: description

Nested collections

New in v2

A collection can show its entries as a folder tree and let editors organise them in subfolders. Entries are stored as Hugo branch bundles (an _index file in a folder of its own), so a page can carry children and page resources.

Set the depth globally, or per collection when only one of them is a tree:

/config/_default/params.yaml

admin:
  nested:
    depth: 2 # every collection
  collections:
    docs:
      nested:
        depth: 4 # this one alone, overriding the global

The depth counts path segments below the collection folder, and is what limits how deep an editor may go.

Left undefined, every collection is flat but two: pages is a tree five levels deep, and indexes two levels deep.

DepthResult
1Nothing emitted: a flat collection
2Folder tree, one level of children
3 and upFolder tree, plus a parent field so an editor picks where a page goes and moves it later

Sveltia CMS

Nested collections used to be Decap-only. Sveltia CMS supports them now, and fixes several long-standing problems of the Decap implementation along the way: entry paths, preview paths, media folders, folder labels and i18n.

The parent field differs between the two: Decap gets a custom Hugolify widget, Sveltia its own folder picker. Hugolify writes the Decap widget and label either way, and Sveltia accepts both for compatibility and ignores them, so the same config serves the two.

Pages CMS

Pages CMS shows a nested collection in its tree layout, the _index.md of each folder standing for the folder. It has no parent field: a page stays in the folder it was created in.

The menus collection

New in v2

The menus collection holds the navigation files, written to /data/menu/. Pick which ones it shows with files:

/config/_default/params.yaml

admin:
  collections:
    menus:
      files: [nav-header-primary, nav-footer-primary, nav-legal, nav-social]

Left undefined, the collection ships nav-header-primary, nav-header-secondary, nav-footer-primary, nav-footer-secondary, nav-legal and nav-social.

FileData fileRendered in
nav-header-primary/data/menu/header/primary.ymlHeader, with dropdowns
nav-header-secondary/data/menu/header/secondary.ymlHeader
nav-footer-primary/data/menu/footer/primary.ymlFooter
nav-footer-secondary/data/menu/footer/secondary.ymlFooter
nav-legal/data/menu/legal.ymlFooter
nav-social/data/menu/social.ymlFooter, as icons

A link takes a title, a URL and a new tab option. It can be highlighted as a button, and hold sub-links, which the primary header menu shows as a dropdown. The footer menus also take a title, shown above their links, and the social menu takes a rel instead of the highlight and the sub-links.

See menus

Menus listed in config

The menus used to be files of the config collection. A project that lists nav-* files in config.files moves them to menus.files: left there, a menu shows in both collections. A project that sets collection_groups names menus next to config, or the collection joins '*'.

The config collection

The config collection holds the other site files an editor may change: banner, footer, credit, organization, structured data, SEO, llms.txt. Pick which ones it shows with files:

/config/_default/params.yaml

admin:
  collections:
    config:
      files: [banner, footer, credit, blocks, seo]

Left undefined, the collection ships banner, footer, credit, organization, schema, seo, llms and llms-full.

The footer accepts blocks, not just an information text.

Default appearance per block type

New in v2

Add blocks to files and the collection gains a file written to /data/blocks.yml, holding the default ui of each block type. It has the shape a block takes in its front matter, and is read key by key, so a field left empty is ignored rather than erasing a default.

The form is generated from admin.blocks.enable, one collapsible section per block type, with the selected-* variants expanded per enabled collection exactly as the block picker expands them. A section arrives open when it already carries a value.

Each section offers the layouts of its block type, as the form of the block offers them, and the carousel settings when the carousel is one of them: a default carousel is set here like a default scroll snap. Every field shows whatever the layout. In the form of a block, choosing a layout reveals the fields that override these defaults, see Fields that depend on another.

Unlike the rest of the config collection, this file is not translated: how a block looks is not language content. Decap CMS saves it keyed by the default language all the same, which the theme reads as well, see With Decap CMS.

See defaults per block

Organization and structured data

New in v2

organization edits /data/organization.yml, the identity of the organization behind the site: legal name, other names, founding date, headcount, languages, areas served, head office and other places, awards, identifiers. The rest of the site may show it too, not only the structured data.

schema edits /data/schema.yml, what only the structured data reads: the schema.org type of the organization, a logo search engines can read, its pages elsewhere, the page listing its offices, and whether email addresses are published.

Like blocks, neither file is translated. The slogan of the organization, translated like the description, goes in seo.

See structured data

llms.txt

New in v2

llms and llms-full edit /data/llms.yml and /data/llms-full.yml, the bodies of the /llms.txt and /llms-full.txt files the theme writes at the root of the site. A body left empty publishes no file.

Like blocks, these files are not translated: agents look for one file at the root, so every language goes in it.

See llms.txt

A file of your own

Each name of files reads the partial of the same name in admin/datas/. Add one to edit a data file of your own, here /data/contact.yml:

layouts/
└── _partials/
    └── admin/
        └── datas/
            └── contact.yml
{{- $fields := slice "title" "text_markdown" -}}
{{- $fields = partial "admin/func/GetFields.html" (dict "file" "contact" "fields" $fields) -}}
{
  label: 'Contact',
  name: 'contact',
  file: 'data/contact.yml',
  i18n: true,
  {{- partial "admin/fields/_fields.yml" $fields -}}
}

Then list it in files, where it should appear: files: [banner, footer, contact, seo]. The same goes for the menus collection. GetFields lets admin.files.contact.fields replace the fields, as for the files of the module, see The fields of a config file.

Create a collection

With params

A collection the admin does not know takes a generic type: its entries go to content/<name>, each in a folder of its own, with the fields of a page. Here a recipes collection:

/config/_default/params.yaml

admin:
  collections:
    recipes:
      enable: true
      fields: # optional, the fields of a page by default
        - draft
        - title_page
        - description
        - featured_image
        - body

Its label comes from the i18n files of the project, admin.collections.recipes.label, or else from its name. The site renders the section with its own templates, or the default ones of the theme.

With a file

For full control, declare the collection in a file named after it:

layouts/
└── _partials/
    └── admin/
        └── collections/
            └── types/
                └── recipes.yml
{
  name: 'recipes',
  folder: 'content/recipes',
  label: '{{ i18n "admin.collections.recipes.label" 2 }}',
  label_singular: '{{ i18n "admin.collections.recipes.label" 1 }}',
  i18n: true,
  {{ partial "admin/collections/config/create.yml" . }}
  {{ partial "admin/collections/config/preview.yml" . }}
  {{ partial "admin/collections/config/path.yml" . }}
  {{ partial "admin/collections/config/slug.yml" . }}

  {{ partial "admin/collections/types/_default.yml" . }}
}

Each partial writes one option the way the CMS in use reads it, and nothing when the CMS has no equivalent. _default.yml adds all the other options. A file listing them itself can leave out the ones the collection does not need:

{{ partial "admin/collections/config/icon.yml" . }}
{{ partial "admin/collections/config/index_file.yml" . }}
{{ partial "admin/collections/config/filter.yml" . }}
{{ partial "admin/collections/config/exclude.yml" . }}
{{ partial "admin/collections/config/reorder.yml" . }}
{{ partial "admin/collections/config/bulk_edit.yml" . }}
{{ partial "admin/collections/config/sortable_fields.yml" . }}
{{ partial "admin/collections/config/summary.yml" . }}
{{ partial "admin/collections/config/summary_fields.yml" . }}
{{ partial "admin/collections/config/thumbnail.yml" . }}
{{ partial "admin/collections/config/view.yml" . }}
{{ partial "admin/collections/config/view_filters.yml" . }}
{{ partial "admin/collections/config/view_groups.yml" . }}
{{ partial "admin/collections/config/fields.yml" (dict "collection" .) }}
{{ partial "admin/collections/config/nested.yml" . }}

view.yml writes the view of Pages CMS, built from summary_fields, sortable_fields and nested.

The fields come from admin.collections.recipes.fields, or else from a file returning the default list, admin/collections/fields/recipes.fields.default.html.

See examples in repository