Slugs and File Paths
An entry’s slug is its filename, and the file path is where that file is stored under the collection’s folder. Both can be customized with the options described on this page.
Entry Slugs
Sveltia CMS provides several ways to customize the slug (filename) of an entry in a collection.
Global Slug Options
The slug option defined at the top-level of the configuration file applies to all collections. The default settings are as follows:
slug:
encoding: unicode
clean_accents: false
sanitize_replacement: '-'
trim: true
lowercase: true
timezone: utc[slug]
encoding = "unicode"
clean_accents = false
sanitize_replacement = "-"
trim = true
lowercase = true
timezone = "utc"{
"slug": {
"encoding": "unicode",
"clean_accents": false,
"sanitize_replacement": "-",
"trim": true,
"lowercase": true,
"timezone": "utc"
}
}{
slug: {
encoding: "unicode",
clean_accents: false,
sanitize_replacement: "-",
trim: true,
lowercase: true,
timezone: "utc",
},
}The available options are:
encoding: Specifies the encoding method for slugs. Supported values areunicode(default) andascii.unicode: Allows Unicode characters in slugs, preserving non-Latin scripts.ascii: Sanitizes slugs to ASCII characters only. The allowed characters are 0-9, a-z, A-Z, hyphen (-) underscore (_) and tilde (~). Other characters are replaced with the value specified in thesanitize_replacementoption.
clean_accents: A boolean value indicating whether to remove accents from characters in slugs. If enabled, accented characters are converted to their unaccented equivalents (e.g.,ébecomese). Also, certain characters like German umlauts are transliterated to their ASCII equivalents (e.g.,ßbecomesss). The default value isfalse.- See the
transliteratelibrary’s replacements list for details on how specific characters are transliterated. Additional rules are applied when i18n support is enabled for specific locales.
- See the
sanitize_replacement: A string used to substitute invalid characters. The default value is a hyphen (-).maxlength: An integer specifying the maximum length of the slug. If the generated slug exceeds this length, it will be truncated. This is useful for CI/CD services or filesystems that impose filename length restrictions. The default value isundefined, meaning there is no length limit.trim: A boolean value indicating whether to trim leading and trailingsanitize_replacementcharacters from the slug. The default value istrue.lowercase: A boolean value indicating whether to convert the slug to lowercase. The default value istrue. Changing this tofalsewill preserve the original casing of the title or identifier field.timezone: A string specifying the timezone to use when generating date-based slugs with template tags like{{day}}and{{hour}}. The default value isutc. You can set this tolocalto use the local timezone of the user.
Deprecation Notice
The collection-level slug_length option has been deprecated in favor of the maxlength global slug option described above. The slug_length option will be removed in Sveltia CMS v1.0.0. If you are upgrading from an older version, update your configuration accordingly.
How Slugs are Generated
By default, Sveltia CMS uses the title field as the slug (filename) for entries in a collection.
If a collection only has the Markdown body field, an entry slug will be generated from a header in the body, if exists. This aims to support a typical VitePress or Docusaurus setup. If no title or header is found, a part of a random UUID will be used to ensure uniqueness.
Specifying an Identifier Field
If you want to use a different field as the entry identifier for generating slugs and filenames, you can specify it using the identifier_field option in the collection definition. This is useful when your entries have a unique identifier field other than title, such as name or id. For example, to use a product_name field as the identifier:
collections:
- name: products
label: Products
folder: /content/products
identifier_field: product_name
fields:
- { name: product_name, label: Product Name }
- { name: description, label: Description, widget: richtext }[[collections]]
name = "products"
label = "Products"
folder = "/content/products"
identifier_field = "product_name"
[[collections.fields]]
name = "product_name"
label = "Product Name"
[[collections.fields]]
name = "description"
label = "Description"
widget = "richtext"{
"collections": [
{
"name": "products",
"label": "Products",
"folder": "/content/products",
"identifier_field": "product_name",
"fields": [
{ "name": "product_name", "label": "Product Name" },
{ "name": "description", "label": "Description", "widget": "richtext" }
]
}
]
}{
collections: [
{
name: "products",
label: "Products",
folder: "/content/products",
identifier_field: "product_name",
fields: [
{ name: "product_name", label: "Product Name" },
{ name: "description", label: "Description", widget: "richtext" },
],
},
],
}Defining Entry Slugs
The slug option allows you to define a custom template for generating entry slugs using various template tags and field names.
For example, to create slugs that include the year and month of creation along with the entry slug, you can use the following configuration:
collections:
- name: posts
label: Blog Posts
folder: /content/posts
slug: '{{year}}-{{month}}-{{slug}}'[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
slug = "{{year}}-{{month}}-{{slug}}"{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"slug": "{{year}}-{{month}}-{{slug}}"
}
]
}{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
slug: "{{year}}-{{month}}-{{slug}}",
},
],
}Any field name defined in the collection’s fields option can be used as a template tag in the slug option. For example, if you have a date field in the collection, you can use {{date}} in the slug option to include the date in the slug. For nested fields, use dot notation, e.g. {{author.name}}.
If a field’s name matches one of the predefined template tags, such as slug, year or uuid, you need to prefix it with fields., like {{fields.slug}}, to avoid confusion with the tag itself.
You can use string transformations with these template tags as well. For example, to create slugs that include the full date in YYYY-MM-DD format along with a custom slug field, you can use the following configuration:
collections:
- name: posts
label: Blog Posts
folder: /content/posts
slug: "{{date | date('YYYY-MM-DD')}}-{{fields.slug}}"[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
slug = "{{date | date('YYYY-MM-DD')}}-{{fields.slug}}"{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"slug": "{{date | date('YYYY-MM-DD')}}-{{fields.slug}}"
}
]
}{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
slug: "{{date | date('YYYY-MM-DD')}}-{{fields.slug}}",
},
],
}TIP
The slug option value should not contain slashes (/). If you need to create a nested folder structure for entries, use the path option instead.
Slug Template Tags
The following template tags are supported in the slug option:
{{slug}}: The slugified version of the entry’stitlefield (or the field defined with theidentifier_fieldoption).{{year}}: 4-digit year of the entry creation date.{{month}}: 2-digit month of the entry creation date.{{day}}: 2-digit day of the entry creation date.{{hour}}: 2-digit hour of the entry creation date.{{minute}}: 2-digit minute of the entry creation date.{{second}}: 2-digit second of the entry creation date.
By default, the entry creation date is based on the UTC timezone for backward compatibility with Netlify/Decap CMS. To use the local timezone of the user instead, set the timezone option to local in the global slug options.
Additionally, the following unique identifier tags are available. These tags generate random values for each entry, ensuring uniqueness. This is particularly useful when the entry title may change later or when the title contains characters that are not suitable for filenames, such as non-Latin scripts.
{{uuid}}: A random UUID v4, e.g.4fc0917c-8aea-4ad5-a476-392bdcf3b642{{uuid_short}}: The last 12 characters of a random UUID v4, e.g.392bdcf3b642.{{uuid_shorter}}: The first 8 characters of a random UUID v4, e.g.4fc0917c.
Making Slugs Editable
By default, an entry’s slug is generated from the title field or the template defined in the slug option, and users never see it while writing. To have users choose the slug themselves instead, set the slug option to the special {{fields._slug}} tag.
With that option in place, a required Slug field appears above the other fields in the Edit Pane. It looks like a standard string field, but its value becomes the entry slug. The field starts out empty, so the entry can’t be saved until a slug has been entered, and slashes and whitespace are rejected.
Only while creating an entry
The Slug field is shown only while an entry is being created, including when an existing entry is duplicated. Once the entry has been saved, the field disappears.
A saved entry can still be renamed, but only with the Slug Editor in the 3-dot menu of the Content Editor. Renaming moves the entry’s file and rewrites every reference to it, so it’s deliberately kept out of the Edit Pane, where it could otherwise be changed by accident in the middle of routine editing.
In an i18n-enabled collection, {{fields._slug}} makes the slug editable in the default locale only, and the remaining locales show the same value as read-only. To let users enter a different slug for each locale, set the slug option to {{fields._slug | localize}} instead.
File Paths
Sveltia CMS provides a couple of options to customize the file paths of entries in a collection.
Using Subfolders
By default, Sveltia CMS saves entries directly under the specified folder using the slug as the filename. However, you can organize entries into subfolders using the path option.
Just like the slug option described above, the path option can use template tags to create dynamic folder structures. The slug template tags and string transformations can be used in the path option, along with any field names defined in the collection’s fields option. For nested fields, use dot notation, e.g. {{author.name}}.
For example, to save blog posts in subfolders based on the year and month of creation, you can use the following configuration:
collections:
- name: posts
label: Blog Posts
folder: /content/posts
path: '{{year}}/{{month}}/{{slug}}'[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
path = "{{year}}/{{month}}/{{slug}}"{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"path": "{{year}}/{{month}}/{{slug}}"
}
]
}{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
path: "{{year}}/{{month}}/{{slug}}",
},
],
}With the above configuration, a blog post created on June 15, 2025, with the title “My First Post” will be saved at content/posts/2025/06/my-first-post.md.
Creating Page Bundles
You can create nested structures like Hugo’s page bundles, or Zola’s asset colocation, which uses the same index.md convention, using the path, media_folder and public_folder options together. For example, to create a leaf bundle for each blog post, you can use the following configuration:
collections:
- name: posts
label: Blog Posts
folder: /content/posts
path: '{{slug}}/index'
media_folder: ''
public_folder: ''[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
path = "{{slug}}/index"
media_folder = ""
public_folder = ""{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"path": "{{slug}}/index",
"media_folder": "",
"public_folder": ""
}
]
}{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
path: "{{slug}}/index",
media_folder: "",
public_folder: "",
},
],
}With the above configuration, a blog post with the title “My First Post” will be saved at content/posts/my-first-post/index.md, and its media files will be stored in the same folder.
The same options can be combined with the nested option to build a tree of page bundles, where each entry has both its own media folder and its own child entries. See Nesting Page Bundles.
Constructing File Paths
A folder collection’s file path is determined by multiple factors: the i18n, folder, path, slug and extension options. The configuration can be complex, especially with i18n support, so let’s break it down.
- The
i18nglobal or collection option (optional)- It can be configured to add internationalization (i18n) support to your site.
- The
structureandomit_default_locale_from_file_pathoptions affect the entry file path.
- The
foldercollection option (required)- It specifies the folder where the collection entries are stored, relative to the repository’s root directory.
- It can contain slashes to create a nested folder structure.
- The
pathcollection option (optional)- It defaults to
{{slug}}, which is theslugcollection option value. - It can contain template tags.
- It can also contain slashes to create a nested folder structure.
- It defaults to
- The
slugcollection option (optional)- It defaults to
{{title}}, which is the entry’stitlefield value’s slugified version. - It can contain template tags but cannot contain slashes.
- It defaults to
- The
extensioncollection option (optional)- It defaults to
md.
- It defaults to
Looking at the above options, the entry file path can be constructed as follows:
- With i18n disabled:yaml
/<folder>/<path>.<extension> - With the
single_filei18n structureyaml/<folder>/<path>.<extension> - With the
multiple_filesi18n structure:yamlWhen the/<folder>/<path>.<locale>.<extension>omit_default_locale_from_file_pathi18n option is set totrue, the path depends on the locale:yaml/<folder>/<path>.<extension> # default locale /<folder>/<path>.<locale>.<extension> # other locales - With the
multiple_foldersi18n structure:yamlWhen the/<folder>/<locale>/<path>.<extension>omit_default_locale_from_file_pathi18n option is set totrue, the path depends on the locale:yaml/<folder>/<path>.<extension> # default locale /<locale>/<folder>/<path>.<extension> # other locales - With the
multiple_root_foldersi18n structure:yamlWhen the/<locale>/<folder>/<path>.<extension>omit_default_locale_from_file_pathi18n option is set totrue, the path depends on the locale:yaml/<folder>/<path>.<extension> # default locale /<locale>/<folder>/<path>.<extension> # other locales