---
url: /en/docs/architecture.md
description: >-
Explore Sveltia CMS architecture, unique features, and advantages over other
headless CMSs.
---
# Architecture
Sveltia CMS inherits a unique architecture from Netlify CMS (now Decap CMS) that sets it apart from traditional content management systems and even other headless CMSs. Netlify itself touted it as “a different kind of CMS”, and Sveltia CMS follows that philosophy closely while introducing its own innovations.
This document explores the architecture of headless CMSs in general, highlighting key distinctions among them, and then delves into the specific architecture of Sveltia CMS to illustrate how it works and what makes it special.
## What Is a Headless CMS?
As a quick refresher, a headless CMS is a content management system without a built-in presentation layer. It provides pure content management decoupled from how that content is displayed, enabling separation of concerns between content and presentation. This architecture offers several key benefits:
* **Flexibility**: Use any frontend framework, static site generator, or platform to consume and display your content
* **Scalability**: Scale your content distribution independently from your presentation layer
* **Performance**: Deliver content through fast, distributed networks without the overhead of traditional CMS presentation layers
* **Security**: Reduce the attack surface by keeping your content API separate from your public-facing application
* **Future-proof**: Change your frontend technology without affecting your content infrastructure
## Types of Headless CMSs
The [headless CMS directory](https://jamstack.org/headless-cms/) on Jamstack.org showcases a wide variety of headless CMSs, each with its own architecture and features. Here are some common architectural distinctions among them:
### API-Driven vs. Git-Based
Most headless CMSs are API-driven, providing REST or GraphQL APIs to fetch and manage content with a backend server for storage. Git-based CMSs use a Git repository as the primary data store, enabling version control, collaboration, and easy change tracking. While API-driven CMSs are scalable for large applications, they require more complex infrastructure.
Git-based CMSs like **Sveltia CMS** are simpler to set up and maintain, avoid vendor lock-in, and are better suited to smaller projects or teams.
### Framework-Agnostic vs. Framework-Specific vs. Built-in SSG
Headless CMSs vary in framework support. Some integrate with specific frameworks for optimized workflows and features, while most are framework-agnostic and work with any generator or framework. A few provide built-in static site generators for direct deployment.
**Sveltia CMS** is [framework-agnostic](/en/docs/frameworks), supporting Astro, Eleventy, Hugo, Jekyll, SvelteKit, and more.
### Cloud vs. Self-Hosted
CMSs are offered as SaaS solutions with provider-managed hosting and subscription pricing, or as self-hosted options for greater control but requiring more expertise.
**Sveltia CMS** is semi-self-hosted: the CMS is served from a CDN (no maintenance needed), but each project has its own instance with content stored in your Git repository for full control.
### Web vs. Desktop
Most headless CMSs are web applications accessible from any device with an internet connection. Some desktop alternatives offer offline capabilities but require installation and updates.
**Sveltia CMS** is the best of both worlds: a web app that runs in the browser while supporting [local file access](/en/docs/workflows/local) via a modern web API.
## How Sveltia CMS Works
Now let’s take a closer look at how Sveltia CMS is architected and what makes it unique among headless CMSs.
### CDN-Served JavaScript
In the [start guide](/en/docs/start), we showed you how to set up Sveltia CMS with just two files:
::: code-group
```html [index.html]
Sveltia CMS
```
```yaml [config.yml]
# yaml-language-server: $schema=https://unpkg.com/@sveltia/cms/schema/sveltia-cms.json
backend:
name: github
repo: user/repo
media_folder: /static/media
public_folder: /media
collections:
- name: posts
label: Posts
folder: /content/posts
fields:
- { label: Title, name: title, widget: string }
- { label: Body, name: body, widget: richtext }
```
:::
Sveltia CMS is distributed as an [npm package](https://www.npmjs.com/package/@sveltia/cms) but is easiest to use via [UNPKG](https://unpkg.com/), a [content delivery network](https://developer.mozilla.org/en-US/docs/Glossary/CDN) (CDN) that serves npm packages. The HTML file is simply a container that loads the Sveltia CMS JavaScript file from there.
Since the CDN always serves the latest version, you never need to manually update the CMS. Just include the script tag and you’re ready to go. The entire CMS — all features and UI — runs within that single JavaScript file. No build step is required.
### Single-Page Application
When you open the HTML file in your browser, Sveltia CMS initializes completely client-side as a [single-page application](https://developer.mozilla.org/en-US/docs/Glossary/SPA) (SPA) using [hash routing](https://developer.mozilla.org/en-US/docs/Glossary/Hash_routing) for navigation. All content processing and user interface rendering happen in your browser without needing a backend server (authentication with GitHub is the only exception).
### YAML Configuration
On startup, Sveltia CMS automatically reads `config.yml` from the same directory as your HTML file — no path specification needed. This configuration file defines your backend, media folders, and content collections.
### Git Backend
Once you authenticate with your Git service provider, you can access and manage content through a user-friendly interface. End-users never need to interact with Git directly; all operations are handled through the provider’s API behind the scenes.
### All Files in One Place
The `index.html` and `config.yml` files live alongside your other project files in your repository, allowing your code, content, assets, CMS instance, and configuration to coexist seamlessly. This simplifies deployment and maintenance, and eliminates the need for a database.
### SSG-Friendly
Sveltia CMS focuses solely on content management without building your site, making it compatible with [any framework](/en/docs/frameworks). It’s particularly well-suited for [static site generators](https://developer.mozilla.org/en-US/docs/Glossary/SSG) (SSGs), which pair naturally with Git-based workflows.
### Local File Access
Sveltia CMS allows developers to [work with local files](/en/docs/workflows/local) directly from the browser, making it easy to update your configuration, content, and media assets without pushing changes to a remote repository first. This also enables offline editing capabilities.
### JavaScript API
Advanced users can leverage the [JavaScript API](/en/docs/api) to customize and extend Sveltia CMS functionality, such as manual initialization, registering custom preview styles, and adding editor components.
## Differences from Netlify/Decap CMS
While Sveltia CMS is heavily inspired by Netlify CMS, we’re committed to building a modern platform with unique features. Key differences include:
* **Built with Svelte**: Smaller bundle sizes, faster performance, fewer crashes, and simpler reactivity compared to React, thanks to efficient compile-time optimizations.
* **Lean codebase**: Provides only the core CMS with no extra packages, reducing complexity and maintenance overhead while enabling frequent releases.
* **Modular dependencies**: Dynamically loads additional dependencies from UNPKG rather than bundling everything into one large file, reducing initial download size.
* **Seamless local workflow**: First CMS to leverage the [File System Access API](https://developer.chrome.com/docs/capabilities/web-apis/file-system-access) for direct browser access to local files, eliminating the need for an insecure proxy server.
* **Modern web application**: Uses [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) for caching, [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) for lazy loading, [View Transition API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) for smooth visual transitions, and more.
* **Startup content fetching**: Enables instant full text search, advanced [relation fields](/en/docs/fields/relation), entry-asset linking, and better performance by fetching all entries in one go.
* **Unified type definitions**: TypeScript types and JSON schema are generated from a single source of truth, ensuring consistency between the codebase and editor support.
## Choosing a Headless CMS
In addition to the architectural differences explained above, here are other factors to consider when choosing a headless CMS:
* **License & Pricing**: open source, commercial, freemium, or enterprise models
* **Activity & Roadmap**: update frequency, new features, bug fixes, and future plans
* **Backend Support**: Git providers, databases, file storage, and API capabilities
* **Content Modeling**: flexible schemas, custom fields, relationships, validations and i18n
* **User Experience**: intuitive UI, personalization, accessibility, performance, and mobile support
* **Security & Access Control**: roles, permissions, SSO, encryption, and vulnerability management
* **Documentation & Support**: tutorials, API reference, examples, FAQs, forums, and professional support
* **Extensibility**: plugins, integrations, APIs, SDKs, and customization options
* **Media Management**: image handling, file uploads, optimization, and gallery features
* **Collaboration Features**: editorial workflows, versioning, comments, and notifications
Sveltia CMS aims to excel in many of these areas while maintaining a simple, developer-friendly experience. We encourage you to [explore its features](/en/docs/features) and see how well it fits your project’s needs!
---
---
url: /en/docs/ui/asset-library.md
description: >-
Organize and manage all media assets in Sveltia CMS with centralized asset
library.
---
# Asset Library
Sveltia CMS’s Asset Library allows you to efficiently manage and organize your media files, including images, videos, and documents. It serves as a centralized hub for all your digital assets, making it easy to upload, categorize, and retrieve files as needed.
::: info Future Plans
Currently, the Asset Library only supports the [internal media storage](/en/docs/media/internal). Support for external media storage providers, such as Cloudinary and Uploadcare, will be added in future releases.
:::
## Features
The Asset Library includes the following features:
### Folder List
Navigate between the global media folder and collection-specific media folders. This allows you to organize assets at both the global level and within individual collections for more granular asset management.
### Asset List
Thumbnails are displayed for image, video and PDF files for easy identification. You can switch between grid and list views, and sort or filter assets by name and file type.
Thumbnails of entries are also displayed in both grid and list views, making it easier to navigate and identify the assets you need.
### Asset Upload
Upload multiple assets at once by browsing or dragging and dropping files directly into the library, including files in nested folders. When you delete an entry or asset file, the empty folder that contains it is also automatically deleted, so you don’t have to clean it up manually.
The CMS prevents the same file from being uploaded twice by comparing file hashes and selecting an existing asset instead.
### Asset Search
Use the search functionality to quickly find specific assets. You can also filter assets by name or file type to narrow down results.
### Asset Details
Preview image, audio, video, text and PDF files directly in the Asset Library. Check your site’s Content Security Policy (CSP) if the preview doesn’t work as expected.
View comprehensive asset details including:
* File size and dimensions
* Commit author and date information
* A list of entries that use the selected asset
* Exif metadata when available, including creation date and GPS coordinates displayed on a map
### Asset Management
Manage your assets with a variety of operations:
* **Rename** existing assets. If the asset is used in any entries, the File and Image fields will be automatically updated with the new file path.
* **Replace** existing assets with new versions.
* **Edit** plain text assets, including Markdown, JSON, SVG files and other text-based content using the built-in editor.
* **Copy** the public URL, file path, text data, or image data of a selected asset to your clipboard.
* **Download** one or more selected assets at once.
* **Delete** one or more selected assets at once.
::: info Future Plans
Image editing capabilities, such as cropping and resizing, will be added in future releases. Advanced DAM features, such as tagging and metadata management, are also planned for future updates.
:::
---
---
url: /en/docs/frameworks/astro.md
description: >-
Learn how to integrate Sveltia CMS with Astro, including starter templates and
real-world examples.
---
# Astro Integration Guide
This guide provides resources and information for integrating Sveltia CMS with [Astro](https://astro.build/), a modern static site builder.
## Starter Templates
Here are some starter templates built by the community using Astro:
* [Astros](https://github.com/majesticooss/astros) by [zanhk](https://github.com/zanhk)
* [Astro i18n Starter](https://github.com/yacosta738/astro-cms) by [yacosta738](https://github.com/yacosta738)
* [astro-sveltia-cms](https://github.com/knolljo/astro-sveltia-cms) by [knolljo](https://github.com/knolljo)
::: info Disclaimer
These third-party resources are not necessarily reviewed by the Sveltia CMS team. We are not responsible for their maintenance or support. Please contact the respective authors for any issues or questions.
:::
## Examples
See real-world examples of Astro integrations in our [Showcase](/en/showcase?framework=astro). Most of the listed sites include links to their source code, so you can explore how they implemented Sveltia CMS with Astro.
## Support for Astro
We have implemented specific features to enhance the integration of Sveltia CMS with Astro:
* The [`value_field`](/en/docs/fields/relation#value-field) Relation field option can contain a locale prefix like `{{locale}}/{{slug}}`, which will be replaced with the current locale. It’s intended to support i18n in Astro. ([Discussion](https://github.com/sveltia/sveltia-cms/discussions/302))
* [Localizing entry slugs](/en/docs/i18n#localizing-entry-slugs): generate localized slugs for multilingual Astro sites, notably with the [@astrolicious/i18n](https://github.com/astrolicious/i18n) library. ([Discussion](https://github.com/sveltia/sveltia-cms/issues/137))
## Development Guide
We’ll be adding a detailed development guide for integrating Sveltia CMS with Astro in the near future. In the meantime, feel free to explore the starter templates and showcase examples for guidance.
---
---
url: /en/docs/backends.md
description: >-
Configure supported Git backends in Sveltia CMS with setup options and best
practices.
---
# Backends
A backend defines where your content is stored and how Sveltia CMS interacts with it. Sveltia CMS primarily supports Git-based backends, allowing seamless integration with popular Git hosting services.
## Supported Backends
Sveltia CMS supports the following Git-based backends:
* [GitHub](/en/docs/backends/github)
* [GitLab](/en/docs/backends/gitlab)
* [Gitea/Forgejo](/en/docs/backends/gitea-forgejo)
For testing purposes, you can also use the [Test Backend](/en/docs/backends/test).
Some features only work with specific backends. For example, [Editorial Workflow](/en/docs/workflows/editorial) currently only works with the GitHub and GitLab backends.
::: warning Breaking changes from Netlify/Decap CMS
Sveltia CMS does not support the **Azure DevOps**, **Bitbucket** and **Git Gateway** backends for performance reasons. Note that [Git Gateway](https://docs.netlify.com/manage/security/secure-access-to-sites/git-gateway/) is now officially deprecated by Netlify. If you use one of these backends in Netlify/Decap CMS, consider switching to GitHub, GitLab, Gitea or Forgejo before migrating to Sveltia CMS.
Also, Sveltia CMS does not support the undocumented custom backend API. The `CMS.registerBackend` method is a noop in Sveltia CMS. We may add support for custom backends in future releases.
:::
## Configuration
All the configuration options for backends can be set in the `backend` option of your CMS configuration file. Here is a basic example of configuring the GitHub backend:
::: code-group
```yaml [YAML]
backend:
name: github
repo: user/repo
```
```toml [TOML]
[backend]
name = "github"
repo = "user/repo"
```
```json [JSON]
{
"backend": {
"name": "github",
"repo": "user/repo"
}
}
```
```js [JavaScript]
{
backend: {
name: "github",
repo: "user/repo",
},
}
```
:::
See the specific backend guides for detailed configuration instructions.
The following sections describe some common configuration options available for all Git-based backends.
### Branch Selection
By default, Sveltia CMS interacts with the repository’s default branch (usually `main` or `master`). You can specify a different branch using the `branch` option in the backend configuration:
::: code-group
```yaml [YAML]{4}
backend:
name: github
repo: user/repo
branch: develop
```
```toml [TOML]{4}
[backend]
name = "github"
repo = "user/repo"
branch = "develop"
```
```json [JSON]{5}
{
"backend": {
"name": "github",
"repo": "user/repo",
"branch": "develop"
}
}
```
```js [JavaScript]{5}
{
backend: {
name: "github",
repo: "user/repo",
branch: "develop",
},
}
```
:::
### Commit Messages
You can customize the Git commit messages used when saving content. The `commit_messages` option allows you to define templates for various actions. Here’s the default configuration:
::: code-group
```yaml [YAML]
backend:
commit_messages:
create: 'Create {{collection}} "{{slug}}"'
update: 'Update {{collection}} "{{slug}}"'
delete: 'Delete {{collection}} "{{slug}}"'
uploadMedia: 'Upload "{{path}}"'
deleteMedia: 'Delete "{{path}}"'
openAuthoring: '{{message}}'
```
```toml [TOML]
[backend.commit_messages]
create = "Create {{collection}} \"{{slug}}\""
update = "Update {{collection}} \"{{slug}}\""
delete = "Delete {{collection}} \"{{slug}}\""
uploadMedia = "Upload \"{{path}}\""
deleteMedia = "Delete \"{{path}}\""
openAuthoring = "{{message}}"
```
```json [JSON]
{
"backend": {
"commit_messages": {
"create": "Create {{collection}} \"{{slug}}\"",
"update": "Update {{collection}} \"{{slug}}\"",
"delete": "Delete {{collection}} \"{{slug}}\"",
"uploadMedia": "Upload \"{{path}}\"",
"deleteMedia": "Delete \"{{path}}\"",
"openAuthoring": "{{message}}"
}
}
}
```
```js [JavaScript]
{
backend: {
commit_messages: {
create: 'Create {{collection}} "{{slug}}"',
update: 'Update {{collection}} "{{slug}}"',
delete: 'Delete {{collection}} "{{slug}}"',
uploadMedia: 'Upload "{{path}}"',
deleteMedia: 'Delete "{{path}}"',
openAuthoring: '{{message}}',
},
},
}
```
:::
The available commit types are:
* `create`, `update`, `delete`: Used when creating, updating, or deleting entries in collections.
* `uploadMedia`, `deleteMedia`: Used when uploading or deleting media assets.
* `openAuthoring`: Used when submitting changes via [open authoring](/en/docs/workflows/open) (fork and pull request).
::: tip
Unlike most of other config options, the commit message keys are camelCased.
:::
#### Available Template Tags
You can use the following template tags in commit messages:
* `{{collection}}`: The `label_singular` or `label` of the collection.
* `{{slug}}`: The slug of the entry.
* `{{path}}`: The file path of the media asset.
* `{{message}}`: The original commit message provided by the user.
* `{{author-email}}`: The email of the signed-in user, if available.
* `{{author-login}}`: The login name of the signed-in user, if available.
* `{{author-name}}`: The display name of the signed-in user, if available.
The following table summarizes which tags are supported for each commit type:
| Commit Type | Supported Tags |
| --- | --- |
| `create`, `update`, `delete` | `collection`, `slug`, `path`, `author-email`, `author-login`, `author-name` |
| `uploadMedia`, `deleteMedia` | `path`, `author-email`, `author-login`, `author-name` |
| `openAuthoring` | `message`, `author-email`, `author-login`, `author-name` |
#### Skipping CI/CD
It’s also possible to add the `[skip ci]` prefix to commit messages to prevent triggering CI/CD pipelines. See the [deployments guide](/en/docs/deployments) for more details.
::: info Future Plans
We plan to add an option that prompts users to enter custom commit messages in the UI before saving changes.
:::
---
---
url: /en/docs/fields/boolean.md
description: Select true/false values in Sveltia CMS using an intuitive toggle switch.
---
# Boolean Field
The Boolean field type allows users to select a true/false value using a toggle switch interface.
## User Interface
### Editor
A toggle switch. It can be turned on or off by clicking or tapping it, like a checkbox.
Additional text can be displayed before or after the switch using the `before_input` and `after_input` options.
### Preview
The preview shows the boolean value as `true` or `false`.
## Data Type
A boolean. If the `required` option is set to `false` and the field is left empty, the value will be `false`.
## Data Validation
No special validation is performed for Boolean fields as the value will always be either `true` or `false`.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the Boolean field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `boolean`.
### Optional Options
#### `default`
* **Type**: `boolean`
* **Default**: `false`
The default value for the field when creating a new entry.
#### `before_input`
* **Type**: `string`
* **Default**: `""`
Text to display before the toggle switch. It’s placed at the `false` position.
#### `after_input`
* **Type**: `string`
* **Default**: `""`
Text to display after the toggle switch. It’s placed at the `true` position.
## Examples
### Basic Boolean Field
Configuration example:
::: code-group
```yaml [YAML]
- name: draft
label: Draft
widget: boolean
default: true
```
```toml [TOML]
[[fields]]
name = "draft"
label = "Draft"
widget = "boolean"
default = true
```
```json [JSON]
{
"name": "draft",
"label": "Draft",
"widget": "boolean",
"default": true
}
```
```js [JavaScript]
{
name: "draft",
label: "Draft",
widget: "boolean",
default: true,
}
```
:::
Output example:
::: code-group
```yaml [YAML]
draft: true
```
```toml [TOML]
draft = true
```
```json [JSON]
{
"draft": true
}
```
:::
---
---
url: /en/docs/media/cloudinary.md
description: >-
Integrate Cloudinary as a media storage provider in Sveltia CMS for efficient
cloud-based asset management.
---
# Cloudinary Integration
Cloudinary is a leading cloud-based media management service that offers comprehensive solutions for image and video upload, storage, manipulation, and delivery. The Cloudinary integration enables users to efficiently manage media assets within Sveltia CMS by leveraging Cloudinary’s powerful features.
## Requirements
* A Cloudinary account. You can sign up for a free account at [cloudinary.com](https://cloudinary.com/).
* Your Cloudinary cloud name and API key. These can be found in your Cloudinary dashboard under the "Account Details" section.
::: tip CSP Settings
If your site uses a Content Security Policy (CSP), You may need to update it to allow requests to Cloudinary. See the [CSP documentation](/en/docs/security#setting-up-content-security-policy) for more details.
:::
## Configuration
### Top-Level Configuration
To configure the Cloudinary media storage in Sveltia CMS, add the following configuration to the top level of your CMS configuration file:
::: code-group
```yaml [YAML]
media_libraries:
cloudinary:
config:
cloud_name: YOUR_CLOUD_NAME
api_key: YOUR_API_KEY
```
```toml [TOML]
[media_libraries.cloudinary]
[media_libraries.cloudinary.config]
cloud_name = "YOUR_CLOUD_NAME"
api_key = "YOUR_API_KEY"
```
```json [JSON]
{
"media_libraries": {
"cloudinary": {
"config": {
"cloud_name": "YOUR_CLOUD_NAME",
"api_key": "YOUR_API_KEY"
}
}
}
}
```
```js [JavaScript]
{
media_libraries: {
cloudinary: {
config: {
cloud_name: "YOUR_CLOUD_NAME",
api_key: "YOUR_API_KEY",
},
},
},
}
```
:::
::: details Legacy `media_library` Option
Sveltia CMS supports the legacy `media_library` option for backward compatibility with Netlify/Decap CMS, but it is recommended to use the `media_libraries` option for new configurations. With the legacy option, only a single media storage provider can be configured. Here is an example of configuring Cloudinary using the legacy option:
```yaml
media_library:
name: cloudinary
config:
cloud_name: YOUR_CLOUD_NAME
api_key: YOUR_API_KEY
```
:::
The `config` object includes the Cloudinary [Media Library widget options](https://cloudinary.com/documentation/media_library_widget#2_set_the_configuration_options). Here are some important notes regarding the configuration options:
* The following parameters are required:
* `cloud_name`: Your Cloudinary cloud name.
* `api_key`: Your Cloudinary API key.
* `default_transformations`: Transformations to apply to all uploaded images. Only the first transformation in the array will be applied to uploaded media in Sveltia CMS. See the [Image transformations](#image-transformations) section below for more details on defining transformations.
* Some options are not applicable in Sveltia CMS and will be ignored if provided, such as `button_caption` and `inline_container`.
::: warning
Do not write your Cloudinary API secret in the configuration file, as it should be kept confidential and not exposed in client-side code. The API key can be used safely for public operations, and Sveltia CMS does not require the API secret for its functionality.
:::
There are two Sveltia CMS-specific configuration options that can be added alongside the `config` object. Both are optional:
* `output_filename_only`: When set to `true`, only the filename will be stored in the CMS instead of the full URL. Defaults to `false`.
* `use_transformations`: Whether to use derived transformation URLs for uploaded media. Defaults to `true`. No effect if `output_filename_only` is `true`.
::: warning Breaking change from Netlify/Decap CMS
The `use_secure_url` option has been removed in Sveltia CMS. All URLs generated by the Cloudinary media storage will use HTTPS by default to ensure secure delivery of media assets.
:::
The complete configuration with these additional options looks like this:
::: code-group
```yaml [YAML]{6-7}
media_libraries:
cloudinary:
config:
cloud_name: YOUR_CLOUD_NAME
api_key: YOUR_API_KEY
output_filename_only: false
use_transformations: true
```
```toml [TOML]{5-6}
[media_libraries.cloudinary]
[media_libraries.cloudinary.config]
cloud_name = "YOUR_CLOUD_NAME"
api_key = "YOUR_API_KEY"
output_filename_only = false
use_transformations = true
```
```json [JSON]{7-8}
{
"media_libraries": {
"cloudinary": {
"config": {
"cloud_name": "YOUR_CLOUD_NAME",
"api_key": "YOUR_API_KEY"
},
"output_filename_only": false,
"use_transformations": true
}
}
}
```
```js [JavaScript]{7-8}
{
media_libraries: {
cloudinary: {
config: {
cloud_name: "YOUR_CLOUD_NAME",
api_key: "YOUR_API_KEY",
},
output_filename_only: false,
use_transformations: true,
},
},
}
```
::::
### Field-Level Configuration
The `media_libraries` configuration can also be specified at the field level for File and Image fields. This allows you to override the top-level configuration for specific fields. Here is an example of configuring a File field to use the Cloudinary media storage with custom default transformations and storing only the filename:
::: code-group
```yaml [YAML]{5-11}
fields:
- name: cover_image
label: Cover Image
widget: image
media_libraries:
cloudinary:
config:
default_transformations:
- - quality: auto
fetch_format: auto
output_filename_only: true
```
```toml [TOML]{5-8}
[[fields]]
name = "cover_image"
label = "Cover Image"
widget = "image"
[fields.media_libraries.cloudinary]
[fields.media_libraries.cloudinary.config]
output_filename_only = true
default_transformations = [[{quality = "auto", fetch_format = "auto"}]]
```
```json [JSON]{7-21}
{
"fields": [
{
"name": "cover_image",
"label": "Cover Image",
"widget": "image",
"media_libraries": {
"cloudinary": {
"config": {
"default_transformations": [
[
{
"quality": "auto",
"fetch_format": "auto"
}
]
]
},
"output_filename_only": true
}
}
}
]
}
```
```js [JavaScript]{7-21}
{
fields: [
{
name: "cover_image",
label: "Cover Image",
widget: "image",
media_libraries: {
cloudinary: {
config: {
default_transformations: [
[
{
quality: "auto",
fetch_format: "auto",
},
],
],
},
output_filename_only: true,
},
},
},
],
}
```
::::
::: details Legacy `media_library` Option
As with the top-level configuration, Sveltia CMS supports the legacy `media_library` option at the field level for backward compatibility. Here is an example of configuring a File field to use the Cloudinary media storage with the legacy option:
```yaml
media_library:
config:
default_transformations:
- - quality: auto
fetch_format: auto
output_filename_only: true
```
:::
## Image Transformations
You can define default image transformations that will be applied to all uploaded images by specifying the `default_transformations` option in the Cloudinary media storage configuration. This option accepts an array of transformation objects, where each object defines a set of transformation parameters. Only the first transformation in the array will be applied to uploaded media in Sveltia CMS.
For example, to resize all uploaded images to a width of 800 pixels and a height of 600 pixels with cropping and automatic gravity, you can configure the `default_transformations` option as follows:
::: code-group
```yaml [YAML]
media_libraries:
cloudinary:
config:
default_transformations:
- - width: 800
height: 600
crop: fill
gravity: auto
```
```toml [TOML]
[media_libraries.cloudinary]
[media_libraries.cloudinary.config]
default_transformations = [[{width = 800, height = 600, crop = "fill", gravity = "auto"}]]
```
```json [JSON]
{
"media_libraries": {
"cloudinary": {
"config": {
"default_transformations": [
[
{
"width": 800,
"height": 600,
"crop": "fill",
"gravity": "auto"
}
]
]
}
}
}
}
```
```js [JavaScript]
{
media_libraries: {
cloudinary: {
config: {
default_transformations: [
[
{
width: 800,
height: 600,
crop: "fill",
gravity: "auto",
},
],
],
},
},
},
}
```
::::
See the [Transformation URL API reference](https://cloudinary.com/documentation/transformation_reference) for a complete list of available transformation parameters and their options.
## Accessing the Storage
The Cloudinary media storage can be accessed through the File and Image fields in Sveltia CMS. When uploading media, files will be stored in your Cloudinary account, and you can take advantage of Cloudinary’s transformation capabilities directly from the CMS. You can also select existing media from your Cloudinary storage.
Users are required to authenticate with Cloudinary using their username and password when accessing the media storage provider. The authentication process is handled automatically by Sveltia CMS using the provided API key.
## Using Transformations in Page Templates
When the `output_filename_only` option is set to `true`, only the filename is stored in your entry data files. To generate the full URL with transformations in your site’s page templates, you can use the [JavaScript SDK](https://cloudinary.com/documentation/javascript_integration) or hardcode [transformed URLs](https://cloudinary.com/documentation/image_transformations) based on your Cloudinary account details. Check the Cloudinary documentation for more information on how to construct URLs with transformations.
---
---
url: /en/docs/fields/code.md
description: >-
Write and edit code snippets in Sveltia CMS with language selection and syntax
highlighting.
---
# Code Field
The Code field type provides a code editor with syntax highlighting for various programming languages. It allows users to write and edit code snippets easily within the CMS.
## User Interface
### Editor
A [Lexical](https://lexical.dev/)-based code editor with syntax highlighting and line numbers.
::: warning Breaking change from Netlify/Decap CMS
Sveltia CMS does not support the theme and keymap inline settings, along with support for some languages, as we have moved away from CodeMirror to Lexical. We may add user settings for themes in the future, after migrating from Prism to Shiki for syntax highlighting.
:::
### Preview
A read-only view of the code snippet with syntax highlighting.
## Data Type
An object with the following structure:
```json
{ "code": "string", "lang": "string" }
```
The object keys can be customized using the `keys` option.
If the `output_code_only` option is set to `true`, the data type will be a string containing only the code.
## Data Validation
* If the `required` option is set to `true`, the code must not be an empty string.
* If the `pattern` option is provided, the code must match the specified regular expression pattern.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the Code field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `code`.
### Optional Options
::: warning Breaking change from Netlify/Decap CMS
Sveltia CMS uses [Prism](https://prismjs.com/) for syntax highlighting. Therefore, the list of supported languages may differ from that of Netlify/Decap CMS, which uses [CodeMirror](https://codemirror.net/). Also, we’ll migrate from Prism to [Shiki](https://shiki.style/) before the 1.0 release to follow Lexical’s migration. The list of supported languages may change again after the migration.
This affects the `default_language` option and the language used in the `default` option, along with the language selection dropdown in the UI.
:::
#### `default`
* **Type**: `object` or `string`
* **Default**: `{ code: "", lang: "" }`
The default value for the field, where `code` is a code snippet and `lang` is any valid programming language supported by [Prism](https://prismjs.com/#supported-languages).
If `output_code_only` is `true`, this should be a string containing the default code.
#### `keys`
* **Type**: `object`
* **Default**: `{ code: "code", lang: "lang" }`
An object that defines the keys used in the data object. The default keys are `code` for the code snippet and `lang` for the programming language.
If `output_code_only` is `true`, this option is ignored.
#### `output_code_only`
* **Type**: `boolean`
* **Default**: `false`
If set to `true`, the field will store and return only the code as a string, instead of an object containing both code and language.
#### `allow_language_selection`
* **Type**: `boolean`
* **Default**: `true`
If set to `false`, the language selection dropdown will be hidden, and the language will default to an empty string or the value specified in the `default` option.
::: tip Note for Netlify/Decap CMS users
The [Netlify/Decap CMS document](https://decapcms.org/docs/widgets/#Code) says the default value for the `allow_language_selection` option is `false`, but it’s actually `true`. The default value in Sveltia CMS is also `true`.
:::
#### `default_language`
* **Type**: `string`
* **Default**: `""`
The default programming language for the code editor. See the [list of supported languages](https://prismjs.com/#supported-languages) on the Prism website for valid values.
## Examples
### Basic Example
The simplest configuration of a Code field:
::: code-group
```yaml [YAML]
- widget: code
label: Code Snippet
name: code_snippet
```
```toml [TOML]
[[fields]]
name = "code_snippet"
label = "Code Snippet"
widget = "code"
```
```json [JSON]
{
"name": "code_snippet",
"label": "Code Snippet",
"widget": "code"
}
```
```js [JavaScript]
{
name: "code_snippet",
label: "Code Snippet",
widget: "code",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
code_snippet:
code: |
function greet() {
console.log("Hello, World!");
}
lang: js
```
```toml [TOML]
[code_snippet]
code = """function greet() {
console.log("Hello, World!");
}"""
lang = "js"
```
```json [JSON]
{
"code_snippet": {
"code": "function greet() {\n console.log(\"Hello, World!\");\n}",
"lang": "js"
}
}
```
:::
### Code Only Output
This example configures the field to output only the code as a string:
::: code-group
```yaml [YAML]
- widget: code
label: Code Only
name: code_only
output_code_only: true
```
```toml [TOML]
[[fields]]
name = "code_only"
label = "Code Only"
widget = "code"
output_code_only = true
```
```json [JSON]
{
"name": "code_only",
"label": "Code Only",
"widget": "code",
"output_code_only": true
}
```
```js [JavaScript]
{
name: "code_only",
label: "Code Only",
widget: "code",
output_code_only: true,
}
```
:::
Output example:
::: code-group
```yaml [YAML]
code_only: |
function greet() {
console.log("Hello, World!");
}
```
```toml [TOML]
code_only = """function greet() {
console.log("Hello, World!");
}"""
```
```json [JSON]
{
"code_only": "function greet() {\n console.log(\"Hello, World!\");\n}"
}
```
:::
### Custom Keys and Default Value
This example customizes the keys used in the data object and sets a default value, with language selection disabled:
::: code-group
```yaml [YAML]
- widget: code
label: Custom Code
name: custom_code
allow_language_selection: false
keys:
code: source_code
lang: language
default:
source_code: "console.log('Hello, World!');"
language: js
```
```toml [TOML]
[[fields]]
name = "custom_code"
label = "Custom Code"
widget = "code"
allow_language_selection = false
[keys]
code = "source_code"
lang = "language"
[default]
source_code = "console.log('Hello, World!');"
language = "js"
```
```json [JSON]
{
"name": "custom_code",
"label": "Custom Code",
"widget": "code",
"allow_language_selection": false,
"keys": {
"code": "source_code",
"lang": "language"
},
"default": {
"source_code": "console.log('Hello, World!');",
"language": "js"
}
}
```
```js [JavaScript]
{
name: "custom_code",
label: "Custom Code",
widget: "code",
allow_language_selection: false,
keys: {
code: "source_code",
lang: "language",
},
default: {
source_code: "console.log('Hello, World!');",
language: "js",
},
}
```
:::
Output example:
::: code-group
```yaml [YAML]
custom_code:
source_code: "console.log('Hello, World!');"
language: js
```
```toml [TOML]
[custom_code]
source_code = "console.log('Hello, World!');"
language = "js"
```
```json [JSON]
{
"custom_code": {
"source_code": "console.log('Hello, World!');",
"language": "js"
}
}
```
:::
---
---
url: /en/docs/collections.md
description: Configure entry and file collections in Sveltia CMS for managing your content.
---
# Collections
The `collections` option allows you to define groups of related content in your project. It’s an array of objects, each representing a collection with specific properties.
There are two types of collections, as well as dividers to visually separate sections in your navigation. These can be mixed as needed.
## Collection Types
There are two main types of collections in Sveltia CMS:
* [Entry Collections](/en/docs/collections/entries): Used for managing multiple entries of similar content, such as blog posts, tags, products and events. Each entry is stored as a separate file in a specified folder.
* [File Collections](/en/docs/collections/files): Used for managing individual files, such as static pages or configuration files. Each file is defined explicitly in the configuration.
Additionally, there is a special type of file collection:
* [Singleton Collection](/en/docs/collections/singletons): Defined at the top level of the config file, singletons are used for managing unique content items, such as site settings or homepage content.
## Designing Content Models with Collections
In Sveltia CMS, collections are fundamental building blocks for creating content models. They help organize and structure your content effectively. See the [Content Modeling Guide](/en/docs/content-modeling) for more information on designing effective content models using collections.
## Creating Collections
Collections are defined in your configuration file under the `collections` property. Here’s how to create both entry and file collections:
::: code-group
```yaml [YAML]{5,12}
collections:
# Entry Collection for Blog Posts
- name: posts
label: Blog Posts
folder: content/posts
fields:
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
# File Collection for Static Pages
- name: pages
label: Pages
files:
- name: about
label: About Page
file: content/pages/about.md
fields:
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]{4,22}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "content/posts"
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
[[collections]]
name = "pages"
label = "Pages"
[[collections.files]]
name = "about"
label = "About Page"
file = "content/pages/about.md"
[[collections.files.fields]]
name = "title"
label = "Title"
[[collections.files.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]{6,15}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "content/posts",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
},
{
"name": "pages",
"label": "Pages",
"files": [
{
"name": "about",
"label": "About Page",
"file": "content/pages/about.md",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
]
}
```
```js [JavaScript]{6,15}
{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "content/posts",
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
{
name: "pages",
label: "Pages",
files: [
{
name: "about",
label: "About Page",
file: "content/pages/about.md",
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
},
],
}
```
:::
The two collections defined above will appear in the Sveltia CMS interface as separate sections for managing blog posts and static pages. These two collection types can be mixed and matched as needed to suit your content management requirements.
It’s easy to distinguish between entry and file collections in the configuration file. Entry collections use the `folder` property to specify the directory where entries are stored, while file collections use the `files` property to define individual files.
There is no hard limit to the number of collections you can define in your configuration file. You can create as many collections as needed to effectively organize and manage your content. However, for optimal user experience, it’s recommended to keep the number of collections manageable and logically grouped.
## Customizing Collection List Appearance
You can customize the appearance of your collection list in Sveltia CMS by adding icons and dividers. This helps improve navigation and organization, especially when you have multiple collections.
### Icons
You can specify an icon for each collection for easy identification in the collection list. You don’t need to install a custom icon set because the Material Symbols font file is already loaded for the application UI. Just pick one of the 2,500+ icons:
1. Visit the [Material Symbols](https://fonts.google.com/icons?icon.set=Material+Symbols\&icon.platform=web) page on Google Fonts.
2. Browse and select an icon, and copy the icon name that appears at the bottom of the right pane.
3. Add it to one of your collection definitions in `config.yml` as the new `icon` property, like the example below.
4. Repeat the same steps for all the collections if desired.
5. Commit and push the changes to your Git repository.
6. Reload Sveltia CMS once the updated config file is deployed.
Here’s an example of adding an icon to an entry collection that manages tags:
::: code-group
```yaml [YAML]{4}
collections:
- name: tags
label: Tags
icon: sell
folder: content/tags
```
```toml [TOML]{4}
[[collections]]
name = "tags"
label = "Tags"
icon = "sell"
folder = "content/tags"
```
```json [JSON]{6}
{
"collections": [
{
"name": "tags",
"label": "Tags",
"icon": "sell",
"folder": "content/tags"
}
]
}
```
```js [JavaScript]{6}
{
collections: [
{
name: "tags",
label: "Tags",
icon: "sell",
folder: "content/tags",
},
],
}
```
:::
### Dividers
With Sveltia CMS, developers can add dividers to the collection list to distinguish between different types of collections. To do so, insert a new item with the `divider` option set to `true`. In VS Code, you may receive a validation error if `config.yml` is treated as a Netlify CMS configuration file. You can resolve this issue by [using our JSON schema](/en/docs/config-basics#json-schema).
::: code-group
```yaml [YAML]{4}
collections:
- name: products
...
- divider: true
- name: pages
...
```
```toml [TOML]{5}
[[collections]]
name = "products"
[[collections]]
divider = true
[[collections]]
name = "pages"
```
```json [JSON]{7}
{
"collections": [
{
"name": "products"
},
{
"divider": true
},
{
"name": "pages"
}
]
}
```
```js [JavaScript]{7}
{
collections: [
{
name: "products",
},
{
divider: true,
},
{
name: "pages",
},
],
}
```
:::
The [singleton collection](/en/docs/collections/files#singletons) also supports dividers.
---
---
url: /en/docs/fields/color.md
description: Select and input colors in Sveltia CMS with an interactive color picker.
---
# Color Field
The Color field type allows users to select and input colors using a color picker interface.
## User Interface
### Editor
The browser’s native color picker.
If the `enableAlpha` option is set to `true`, a slider for selecting the alpha (transparency) channel will also be displayed.
If the `allowInput` option is set to `true`, users can manually enter color values as text.
::: info Future Plans
We plan to enhance the UI with a custom color picker in the future.
:::
### Preview
A small color swatch showing the selected color, along with its RGB(A) hex value and `rgb()` function notation.
## Data Type
A string representing the color in RGB format, e.g. `#RRGGBB`. The value is stored in uppercase letters.
If the `enableAlpha` option is set to `true`, the color will be stored in RGBA format, e.g. `#RRGGBBAA`.
If the `required` option is set to `false` and the field is left empty, the value will be an empty string.
## Data Validation
* If the `required` option is set to `true`, the color value must not be an empty string.
* The color value must be a valid hex color code, either in RGB (`#RRGGBB`) or RGBA (`#RRGGBBAA`) format, depending on the `enableAlpha` option.
* If the `pattern` option is provided, the color value must match the specified regular expression pattern.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the Color field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `color`.
### Optional Options
::: tip
Unlike most of other config options, `enableAlpha` and `allowInput` are camelCased.
:::
#### `default`
* **Type**: `string`
* **Default**: `""` (empty string)
The default color value for the field, e.g. `#FF0000` for red. If `enableAlpha` is `true`, the default value should include the alpha channel, e.g. `#FF0000FF` for opaque red.
#### `enableAlpha`
* **Type**: `boolean`
* **Default**: `false`
If set to `true`, the color picker will allow selection of the alpha (transparency) channel, and the color value will be stored in RGBA format.
#### `allowInput`
* **Type**: `boolean`
* **Default**: `false`
If set to `true`, users can manually enter color values as text in addition to using the color picker.
## Examples
### Basic Color Field
This example shows a simple Color field configuration.
::: code-group
```yaml [YAML]
- name: background_color
label: Background Color
widget: color
```
```toml [TOML]
[[fields]]
name = "background_color"
label = "Background Color"
widget = "color"
```
```json [JSON]
{
"name": "background_color",
"label": "Background Color",
"widget": "color"
}
```
```js [JavaScript]
{
name: "background_color",
label: "Background Color",
widget: "color",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
background_color: '#FFFFFF'
```
```toml [TOML]
background_color = "#FFFFFF"
```
```json [JSON]
{
"background_color": "#FFFFFF"
}
```
:::
### Color Field with Alpha and Input Allowed
This example shows a Color field configuration with alpha channel enabled, manual input allowed, and a default value set.
::: code-group
```yaml [YAML]
- name: overlay_color
label: Overlay Color
widget: color
enableAlpha: true
allowInput: true
default: '#00000080'
```
```toml [TOML]
[[fields]]
name = "overlay_color"
label = "Overlay Color"
widget = "color"
enableAlpha = true
allowInput = true
default = "#00000080"
```
```json [JSON]
{
"name": "overlay_color",
"label": "Overlay Color",
"widget": "color",
"enableAlpha": true,
"allowInput": true,
"default": "#00000080"
}
```
```js [JavaScript]
{
name: "overlay_color",
label: "Overlay Color",
widget: "color",
enableAlpha: true,
allowInput: true,
default: "#00000080",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
overlay_color: '#00000080'
```
```toml [TOML]
overlay_color = "#00000080"
```
```json [JSON]
{
"overlay_color": "#00000080"
}
```
:::
---
---
url: /en/docs/fields/compute.md
description: Display read-only computed values in Sveltia CMS based on other fields.
---
# Compute Field
The Compute field type displays read-only computed values based on other fields in the entry. It automatically updates the displayed value when the dependent fields change.
## User Interface
### Editor
Read-only display of computed values based on other fields in the entry. The value is automatically updated when the dependent fields change.
### Preview
A read-only display of the computed value.
## Data Type
A string representing the computed value.
If the `{{index}}` variable is used within a list, the value will be a number representing the current index of the item in the list.
## Data Validation
No specific data validation is applied to the Compute field, as its value is derived from other fields.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the Compute field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `compute`.
#### `value`
* **Type**: `string`
The value can be computed using a template string that references other fields. It supports:
* A value template that defines how to compute the field’s value. It can include references to other fields using the syntax `{{fields.name}}`, where `name` is the name of the field to reference.
* The special variable `{{index}}` to reference the current index when used within a list. It only works inside a [List field](/en/docs/fields/list).
## Examples
### Basic Example
This example demonstrates a Compute field that concatenates the values of two string fields, `first_name` and `last_name`, to create a `full_name` field.
::: code-group
```yaml [YAML]
fields:
- name: first_name
label: First Name
widget: string
- name: last_name
label: Last Name
widget: string
- name: full_name
label: Full Name
widget: compute
value: '{{fields.first_name}} {{fields.last_name}}'
```
```toml [TOML]
[[fields]]
name = "first_name"
label = "First Name"
widget = "string"
[[fields]]
name = "last_name"
label = "Last Name"
widget = "string"
[[fields]]
name = "full_name"
label = "Full Name"
widget = "compute"
value = "{{fields.first_name}} {{fields.last_name}}"
```
```json [JSON]
{
"fields": [
{
"name": "first_name",
"label": "First Name",
"widget": "string"
},
{
"name": "last_name",
"label": "Last Name",
"widget": "string"
},
{
"name": "full_name",
"label": "Full Name",
"widget": "compute",
"value": "{{fields.first_name}} {{fields.last_name}}"
}
]
}
```
```js [JavaScript]
{
fields: [
{
name: 'first_name',
label: 'First Name',
widget: 'string',
},
{
name: 'last_name',
label: 'Last Name',
widget: 'string',
},
{
name: 'full_name',
label: 'Full Name',
widget: 'compute',
value: '{{fields.first_name}} {{fields.last_name}}',
},
];
}
```
:::
Output example when `first_name` is “John” and `last_name` is “Doe“:
::: code-group
```yaml [YAML]
first_name: John
last_name: Doe
full_name: John Doe
```
```toml [TOML]
first_name = "John"
last_name = "Doe"
full_name = "John Doe"
```
```json [JSON]
{
"first_name": "John",
"last_name": "Doe",
"full_name": "John Doe"
}
```
```js [JavaScript]
{
first_name: 'John',
last_name: 'Doe',
full_name: 'John Doe',
}
```
:::
### Email Link
This example demonstrates a Compute field that generates a mailto link using an email address from another String field.
::: code-group
```yaml [YAML]
fields:
- name: contact_email
label: Contact Email
widget: string
type: email
- name: contact_email_link
label: Contact Email Link
widget: compute
value: 'mailto:{{fields.contact_email}}?subject=Inquiry'
```
```toml [TOML]
[[fields]]
name = "contact_email"
label = "Contact Email"
widget = "string"
type = "email"
[[fields]]
name = "contact_email_link"
label = "Contact Email Link"
widget = "compute"
value = "mailto:{{fields.contact_email}}?subject=Inquiry"
```
```json [JSON]
{
"fields": [
{
"name": "contact_email",
"label": "Contact Email",
"widget": "string",
"type": "email"
},
{
"name": "contact_email_link",
"label": "Contact Email Link",
"widget": "compute",
"value": "mailto:{{fields.contact_email}}?subject=Inquiry"
}
]
}
```
```js [JavaScript]
{
fields: [
{
name: 'contact_email',
label: 'Contact Email',
widget: 'string',
type: 'email',
},
{
name: 'contact_email_link',
label: 'Contact Email Link',
widget: 'compute',
value: 'mailto:{{fields.contact_email}}?subject=Inquiry',
},
];
}
```
:::
Output example when `contact_email` is “contact@example.com“:
::: code-group
```yaml [YAML]
contact_email: contact@example.com
contact_email_link: mailto:contact@example.com?subject=Inquiry
```
```toml [TOML]
contact_email = "contact@example.com"
contact_email_link = "mailto:contact@example.com?subject=Inquiry"
```
```json [JSON]
{
"contact_email": "contact@example.com",
"contact_email_link": "mailto:contact@example.com?subject=Inquiry"
}
```
```js [JavaScript]
{
contact_email: 'contact@example.com',
contact_email_link: 'mailto:contact@example.com?subject=Inquiry';
}
```
:::
### Using `index` in a List
The `{{index}}` variable can be used within a list to reference the current item’s index. In this example, we create a list of items where each item has a computed `index` based on its index in the list.
::: code-group
```yaml [YAML]
fields:
- name: items
label: Items
widget: list
fields:
- name: name
label: Name
widget: string
- name: index
label: Item Index
widget: compute
value: '{{index}}'
```
```toml [TOML]
[[fields]]
name = "items"
label = "Items"
widget = "list"
[[fields.fields]]
name = "name"
label = "Name"
widget = "string"
[[fields.fields]]
name = "index"
label = "Item Index"
widget = "compute"
value = "{{index}}"
```
```json [JSON]
{
"fields": [
{
"name": "items",
"label": "Items",
"widget": "list",
"fields": [
{
"name": "name",
"label": "Name",
"widget": "string"
},
{
"name": "index",
"label": "Item Index",
"widget": "compute",
"value": "{{index}}"
}
]
}
]
}
```
```js [JavaScript]
{
fields: [
{
name: 'items',
label: 'Items',
widget: 'list',
fields: [
{
name: 'name',
label: 'Name',
widget: 'string',
},
{
name: 'index',
label: 'Item Index',
widget: 'compute',
value: '{{index}}',
},
],
},
];
}
```
:::
Output example when three items are added with names “Apple“, “Banana“, and “Cherry“:
::: code-group
```yaml [YAML]
items:
- name: Apple
index: 0
- name: Banana
index: 1
- name: Cherry
index: 2
```
```toml [TOML]
[[items]]
name = "Apple"
index = 0
[[items]]
name = "Banana"
index = 1
[[items]]
name = "Cherry"
index = 2
```
```json [JSON]
{
"items": [
{
"name": "Apple",
"index": 0
},
{
"name": "Banana",
"index": 1
},
{
"name": "Cherry",
"index": 2
}
]
}
```
:::
---
---
url: /en/docs/config-basics.md
description: >-
Configure Sveltia CMS using YAML, TOML, JSON, or JavaScript with schema
validation, autocomplete, and AI tool integration.
---
# Configuration Basics
This guide covers the basics of configuring Sveltia CMS using a configuration file. It explains the supported file formats, how to specify the configuration file location, and how to enable validation and autocomplete in your code editor. It also provides information on using AI tools to assist with configuration.
::: info Future Plans
We plan to introduce a graphical configuration editor in a future release, allowing users to create and modify the configuration directly within the CMS interface. For now, please refer to this guide for manual configuration.
:::
## Supported Formats
Sveltia CMS supports configuration files in the following formats:
### YAML
The CMS configuration file is usually written in YAML format. Ensure that your file adheres to proper YAML syntax to avoid parsing errors. If you are new to YAML, consider reviewing a [YAML tutorial](https://www.redhat.com/en/topics/automation/what-is-yaml) to familiarize yourself with the syntax.
Sveltia CMS currently uses the [`yaml` npm package](https://www.npmjs.com/package/yaml) for parsing and serializing YAML files.
Here are some key YAML syntax features to keep in mind:
#### Comments
YAML supports comments using the `#` symbol. Comments can be placed on their own line or at the end of a line:
```yaml
# This is a comment
title: My Site # This is an inline comment
```
#### Shorthand Notation
Sometimes we use shorthand notation for brevity. For example,
```yaml
fields:
- name: title
label: Title
widget: string
- name: align
label: Alignment
widget: select
options:
- left
- center
- right
```
is the same as
```yaml
fields:
- { name: title, label: Title, widget: string }
- { name: align, label: Alignment, widget: select, options: [left, center, right] }
```
#### Quoting Strings
In YAML, strings can be quoted using single (`'`) or double (`"`) quotes. Quoting is necessary when the string contains special characters, leading/trailing spaces, or when you want to preserve the exact formatting. For example:
```yaml
description: 'A site with special characters: #, :, -'
```
#### Multiline Strings
YAML allows multiline strings using the `|` (literal) or `>` (folded) indicators. For example:
```yaml
description: |
This is a multiline
string that preserves
line breaks.
summary: >
This is a folded multiline string that replaces line breaks with spaces.
```
#### Anchors and Aliases
YAML supports anchors and aliases to reuse configuration snippets. This is an advanced feature that can help reduce duplication. For example:
```yaml
fields:
- &title_field
name: title
label: Title
widget: string
- name: subtitle
label: Subtitle
widget: string
- <<: *title_field
name: headline
label: Headline
```
### TOML
TOML format is also supported for configuration files. If you prefer TOML, create a file named `config.toml` instead of `config.yml` and write the configuration in TOML syntax. Make sure to add a `` tag in your HTML to point to the file with the correct MIME type (see the [Config URL](#toml-or-json-configuration-file) section below).
Sveltia CMS currently uses the [`smol-toml` npm package](https://www.npmjs.com/package/smol-toml) for parsing and serializing TOML files.
### JSON
Sveltia CMS also supports JSON format for configuration files. However, JSON is mainly intended for programmatic generation of configuration files rather than manual editing, due to its verbosity and lack of support for comments.
To use a JSON configuration file, create a file named `config.json` instead of `config.yml` and write the configuration in JSON syntax, and add a `` tag in your HTML to point to the file with the correct MIME type (see the [Config URL](#toml-or-json-configuration-file) section below).
We don’t support JSONC, JSON5, or other JSON variants — only standard JSON that can be parsed by [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) and serialized by [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). If you want to write comments in your configuration file, use YAML format instead.
### JavaScript/TypeScript
Instead of using a static configuration file, you can also provide the CMS configuration as a JavaScript object when [manually initializing](/en/docs/api/initialization) the CMS. It gives you the most flexibility and control over the configuration, allowing you to dynamically generate or modify the configuration based on your application logic.
The field configuration for [custom editor components](/en/docs/api/editor-components) also uses JavaScript objects.
## Config URL
You can customize the configuration file location and format by specifying a URL in your HTML using a `` tag with `rel="cms-config-url"`. This is useful if you want to store the configuration file in a different location or use a different format than the default.
::: warning Configuration is Public
Regardless of the location, the configuration file is publicly accessible on the web server. Avoid including sensitive information, such as API keys or passwords, in the configuration file.
:::
### Custom Configuration File Path
By default, Sveltia CMS looks for a YAML configuration file named `config.yml` located in the same folder as the `index.html` file. The file is typically accessible at `/admin/config.yml` on a web server. There is no need to specify this default location explicitly.
To specify a custom configuration file path, add a `` tag in your HTML’s `` section:
```html
```
The MIME type for YAML files is `application/yaml` (standardized) or `text/yaml` (legacy). Both are supported.
### TOML or JSON Configuration File
If you use a TOML or JSON configuration file instead of YAML, you need to add a `` tag with the appropriate MIME type. This tells Sveltia CMS to load the configuration from the specified file instead of the default `config.yml`. Below are examples for both formats.
```html
```
```html
```
### Multiple Configuration Files
You can specify multiple configuration files by adding multiple `` tags. Sveltia CMS will merge them in the order they appear in the HTML.
```html
```
::: tip Limitations
YAML anchors, aliases and merge keys only work if they are in the same file. This is because the files are parsed as separate JavaScript objects and then merged using the [`deepmerge`](https://www.npmjs.com/package/deepmerge) library.
Also, modularized configuration files may raise errors if you enable JSON schema validation in your code editor, as the schema expects a complete configuration object.
:::
## Validation and Autocomplete
For a better development experience, Sveltia CMS provides JSON schema support and TypeScript types for configuration validation and autocomplete.
### JSON Schema
Sveltia CMS provides a full [JSON schema](https://json-schema.org/) for the configuration file, so you can get autocomplete and validation in your favorite code editor while editing the CMS configuration. The schema is generated from the source and always up to date with the latest CMS version.
#### Enabling JSON Schema Validation in VS Code
If you use VS Code, you can enable it for the YAML configuration file by installing the [YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) and adding the following comment to the top of `config.yml`:
```yaml
# yaml-language-server: $schema=https://unpkg.com/@sveltia/cms/schema/sveltia-cms.json
```
For TOML files, install the [Even Better TOML extension](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml) and add the following comment to the top of `config.toml`:
```toml
#:schema https://unpkg.com/@sveltia/cms/schema/sveltia-cms.json
```
JSON files have native support in VS Code, so no extension is needed. Just add the following line to the top of `config.json`, within the curly braces:
```json
"$schema": "https://unpkg.com/@sveltia/cms/schema/sveltia-cms.json",
```
::: details Workspace-level configuration
Instead of adding the schema comment to the top of the configuration file, you can also set it up at the workspace level in VS Code. Add the following to your project’s [VS Code settings file](https://code.visualstudio.com/docs/configure/settings#_settings-json-file) at `.vscode/settings.json`, within the outer curly braces.
For YAML files:
```jsonc
"yaml.schemas": {
"https://unpkg.com/@sveltia/cms/schema/sveltia-cms.json": ["/static/admin/config.yml"]
}
```
For JSON files:
```jsonc
"json.schemas": [
{
"fileMatch": ["/static/admin/config.json"],
"url": "https://unpkg.com/@sveltia/cms/schema/sveltia-cms.json"
}
]
```
The configuration file location varies by framework and project structure, so adjust the path accordingly. For example, if you use Astro, the file is typically located in the `/public/admin/` directory.
:::
#### Other Editors
Check your code editor or IDE documentation to see if it supports JSON schema validation for YAML, TOML, or JSON files. If supported, use the following schema URL:
```
https://unpkg.com/@sveltia/cms/schema/sveltia-cms.json
```
[WebStorm](https://www.jetbrains.com/help/webstorm/yaml.html#json_schema) and other JetBrains IDEs have built-in support for JSON schema validation in YAML and JSON files. You can configure the schema in the IDE settings.
### TypeScript Support
When using the `@sveltia/cms` package in a TypeScript-enabled project, you can get type checking and autocomplete for the [API](/en/docs/api), including the configuration object when [manually initializing](/en/docs/api/initialization) the CMS.
The type definitions are generated from the JSDoc comments in the source code, ensuring they are accurate and up to date with the latest CMS version.
### Runtime Validation
Sveltia CMS performs runtime validation of the configuration file when the CMS initializes. If there are any errors in the configuration, they will be displayed on the login screen.
This helps catch issues early and ensures that the CMS operates with a valid configuration, preventing potential runtime errors. The runtime validation includes checks for:
* Common backend misconfigurations
* File format and extension mismatches
* Invalid or duplicate collection or field names
* Mutually exclusive config options
* Invalid references in Relation fields
::: warning Unfinished feature
Comprehensive runtime validation is still under development and may not cover all configuration options yet. We recommend using JSON schema validation in your code editor for the most reliable feedback while editing the configuration file.
:::
## AI Tools Support
We provide [standard `llms.txt` files](https://llmstxt.org/) to help AI tools understand the structure and syntax of the Sveltia CMS configuration. Generated from this documentation site, these files can be used with AI code assistants, such as GitHub Copilot, ChatGPT, Claude, and others, to enhance their ability to help you write and edit the configuration file.
::: warning Work-in-Progress
This site is still under development, and the documentation may contain inaccurate or incomplete information. The `llms.txt` files are automatically generated from the documentation site, so they may also contain errors or omissions. We’ll continue to improve the documentation and the `llms.txt` files over time.
:::
### Available `llms.txt` Files
There are two versions of the Sveltia CMS documentation available as `llms.txt` files:
* [`llms.txt`](https://sveltiacms.app/llms.txt): A sitemap-style version of the documentation, including the title and description of each page. (3k+ tokens)
```
https://sveltiacms.app/llms.txt
```
* [`llms-full.txt`](https://sveltiacms.app/llms-full.txt): A complete version of the documentation, including all page content, code examples, and details. (200k+ tokens)
```
https://sveltiacms.app/llms-full.txt
```
### Using the `llms.txt` Files
If you use GitHub Copilot in VS Code, you can use the [`#fetch` tool](https://code.visualstudio.com/docs/copilot/chat/copilot-chat-context#_reference-content-from-the-web) in the chat interface to load the `llms.txt` file as context:
```
#fetch https://sveltiacms.app/llms.txt
```
If you use Cursor, you can add the `llms.txt` file using the [`@docs` feature](https://cursor.com/docs/context/mentions#adding-your-own-documentation).
For other AI tools, refer to their documentation on how to add custom knowledge sources or context files.
---
---
url: /en/docs/ui/content-editor.md
description: >-
Create and manage content entries in Sveltia CMS with auto-saving drafts,
preview synchronization, keyboard shortcuts, and multilingual support.
---
# Content Editor
Sveltia CMS provides a powerful content editor that allows users to create and modify content entries stored in their Git repository. This document outlines the key features and functionalities of the content editor.
## Features
The content editor includes the following features to enhance the content creation and editing experience:
### Auto-Saving Drafts
When creating or editing content, Sveltia CMS automatically saves draft backups in the browser’s local storage. This ensures that your work is not lost in case of accidental navigation away from the page or browser crashes. Drafts are saved periodically as you make changes and can be restored when you return to the editing interface.
Auto-saving draft can be disabled in User Preferences.
### Scroll Synchronization
When editing long entries, Sveltia CMS synchronizes the scroll position between the editor and the preview pane. This helps you see how your content will look as you write, without having to manually scroll both sections.
### Click-to-Highlight
Clicking on a field in the Preview Pane highlights the corresponding field in the Edit Pane. If the field is collapsed in the Edit Pane, it will automatically expand when clicked in the Preview Pane. This feature makes it easy to locate and edit specific fields based on their appearance in the preview.
### Revert Changes
The content editor includes Revert buttons that allow you to discard all unsaved changes or revert individual fields to their last saved state. This feature is useful if you want to undo changes made during the current editing session.
### View on Live Site
The 3-dot menu in the content editor includes a View on Live Site option. This allows you to quickly open the live version of the entry you are editing, making it easy to check how the current content appears on the actual website.
### View Source
When Developer Mode is enabled, the 3-dot menu in the content editor provides a View Source option. This allows you to quickly open the source file of the entry or asset in your Git repository, making it easy to review or edit the raw content.
### I18n Support
If [internationalization](/en/docs/i18n) (i18n) is enabled in your Sveltia CMS configuration, the content editor provides support for managing translations of your content. You can switch between different language versions of the content you are editing, making it easy to create and maintain multilingual sites.
* **Language Switcher**: A language switcher is available in the editor interface, allowing you to select the desired language for editing and preview. If there are any errors or missing translations, they will be indicated in the switcher.
* **Translate Button**: A Translate button is provided to translate all or specific text-type fields using a third-party [translation service](/en/docs/integrations/translations). This feature can help speed up the process of creating translations for your content.
* **Copy Button**: A Copy button is available to copy content from one language version to another, facilitating the translation process.
See also the [Linking to Content Editor](#linking-to-content-editor) section for information on setting the editor pane locale via URL.
### Keyboard Shortcuts
Sveltia CMS includes several keyboard shortcuts to enhance productivity while editing content.
* Save an entry: `Ctrl+S` (Windows/Linux) or `Command+S` (macOS)
* Cancel entry editing: `Escape`
Standard keyboard shortcuts are also available in the Markdown editor, including `Ctrl+B`/`Command+B` for bold text, `Ctrl+I`/`Command+I` for italics, and `Tab` to indent a list item.
## Linking to Content Editor
Sveltia CMS allows you to link directly to specific states of the content editor using URL query parameters. This can be useful for sharing links to specific entries or pre-filling fields when creating new entries.
### Opening Specific Entries
You can link directly to the content editor for a specific entry in an [entry collection](/en/docs/collections/entries) using the following URL format:
```
https://YOUR_DOMAIN/admin/#/collections/COLLECTION_NAME/entries/ENTRY_ID
```
### Dynamic Default Values
Sveltia CMS supports dynamic default values passed with URL query parameters. This allows pre-filling certain fields when creating new entries in an [entry collection](/en/docs/collections/entries).
The URL format for pre-filling fields is as follows:
```
https://YOUR_DOMAIN/admin/#/collections/COLLECTION_NAME/new?field1=value1&field2=value2
```
Where `field1`, `field2`, etc. are the names of the fields you want to pre-fill with `value1`, `value2`, etc. Make sure to [URL-encode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) the parameter values. Nested fields can be targeted using dot notation in the field names.
For example, given the following collection configuration:
::: code-group
```yaml [YAML]
collections:
- name: posts
label: Posts
folder: /content/posts
fields:
- name: title
label: Title
- name: author
label: Author
widget: object
fields:
- name: name
label: Name
- name: body
label: Body
widget: richtext
```
```toml [TOML]
[[collections]]
name = "posts"
label = "Posts"
folder = "/content/posts"
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "author"
label = "Author"
widget = "object"
[[collections.fields.fields]]
name = "name"
label = "Name"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]
{
"collections": [
{
"name": "posts",
"label": "Posts",
"folder": "/content/posts",
"fields": [
{ "name": "title", "label": "Title" },
{
"name": "author",
"label": "Author",
"widget": "object",
"fields": [{ "name": "name", "label": "Name" }]
},
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
```
```js [JavaScript]
{
collections: [
{
name: "posts",
label: "Posts",
folder: "/content/posts",
fields: [
{ name: "title", label: "Title" },
{
name: "author",
label: "Author",
widget: "object",
fields: [{ name: "name", label: "Name" }],
},
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
}
```
:::
The following URL will open the new entry editor with the `title`, `author.name` and `body` fields pre-filled:
```
https://example.com/admin/#/collections/posts/new?title=My%20First%20Post&author.name=John%20Doe&body=Hello%2C%20world!
```
### Editor Pane Locale
By default, Sveltia CMS uses the default locale for the Content Editor pane. However, you can specify a different locale for the editor pane using a URL query parameter when [i18n support](/en/docs/i18n) is enabled.
To set the editor pane locale, append the `_locale` query parameter to the CMS URL with the desired locale code. For example, to open the editor pane in French (`fr`), you would use the following URL:
```
https://YOUR_DOMAIN/admin/#/collections/COLLECTION_NAME/entries/ENTRY_ID?_locale=fr
```
For a new entry:
```
https://YOUR_DOMAIN/admin/#/collections/COLLECTION_NAME/new?_locale=fr
```
The query parameter can be combined with [dynamic default values](#dynamic-default-values) to pre-fill field values via URL.
## Saving Behavior
### Save and Publish Options
When the `skip_ci` backend option is enabled, the Save button in the content editor has a dropdown menu that allows you to choose between two saving options. See the [Disabling Automatic Deployments](/en/docs/deployments#disabling-automatic-deployments) section for more details.
### Auto-Close Editor
When you save your changes, the content editor automatically closes the editing interface and returns you to the collection or file list. This streamlines the workflow by reducing the number of clicks needed to return to the main interface after saving. If you prefer to stay in the editor after saving, you can change this behavior in User Preferences.
## Preview Pane
Developers can enhance the content editing experience by providing real-time previews of how the content will appear on the live site. Sveltia CMS offers several options for customizing and controlling the preview feature.
::: info
Please note that, due to the nature of framework-agnostic design, we don’t plan to support live site previews that fetch data from the actual website. If you need this feature, consider using a framework-specific CMS solution.
:::
### Disabling Previews
Previews are enabled by default. However, if you want to disable the preview feature entirely, you can do so at different levels:
#### Global
Add the following configuration to the top level of your `config.yml` file:
::: code-group
```yaml [YAML]
editor:
preview: false
```
```toml [TOML]
[editor]
preview = false
```
```json [JSON]
{
"editor": {
"preview": false
}
}
```
```js [JavaScript]
{
editor: {
preview: false,
},
}
```
:::
#### Collection-Level
Add the same `editor` option to a specific collection in your `config.yml` file:
::: code-group
```yaml{5-6} [YAML]
collections:
- name: blog
label: Blog
folder: /content/blog
editor:
preview: false
```
```toml{5-6} [TOML]
[[collections]]
name = "blog"
label = "Blog"
folder = "/content/blog"
[collections.editor]
preview = false
```
```json{7-9} [JSON]
{
"collections": [
{
"name": "blog",
"label": "Blog",
"folder": "/content/blog",
"editor": {
"preview": false
}
}
]
}
```
```js{7-9} [JavaScript]
{
collections: [
{
name: "blog",
label: "Blog",
folder: "/content/blog",
editor: {
preview: false,
},
},
],
}
```
:::
#### File-Level
Add the same `editor` option to a specific file in your `config.yml` file:
::: code-group
```yaml{5-6}
files:
- name: about
label: About Page
file: /content/about.md
editor:
preview: false
```
```toml{5-6} [TOML]
[[files]]
name = "about"
label = "About Page"
file = "/content/about.md"
[files.editor]
preview = false
```
```json{7-9} [JSON]
{
"files": [
{
"name": "about",
"label": "About Page",
"file": "/content/about.md",
"editor": {
"preview": false
}
}
]
}
```
```js{7-9} [JavaScript]
{
files: [
{
name: "about",
label: "About Page",
file: "/content/about.md",
editor: {
preview: false,
},
},
],
}
```
:::
#### Field-Level
Add the `preview` option to a specific field in your `config.yml` file:
::: code-group
```yaml{5} [YAML]
fields:
- name: body
label: Body
widget: richtext
preview: false
```
```toml{5} [TOML]
[[fields]]
name = "body"
label = "Body"
widget = "richtext"
preview = false
```
```json{7} [JSON]
{
"fields": [
{
"name": "body",
"label": "Body",
"widget": "richtext",
"preview": false
}
]
}
```
```js{7} [JavaScript]
{
fields: [
{
name: "body",
label: "Body",
widget: "richtext",
preview: false,
},
],
}
```
:::
### Advanced Customization
Sveltia CMS allows developers to create custom preview templates and styles to provide a more accurate representation of how the content will appear on the live site.
* [Custom Preview Styles](/en/docs/api/preview-styles): Register custom CSS styles for the preview pane, allowing for better visual fidelity with the live site.
* [Custom Preview Templates](/en/docs/api/preview-templates): Create custom preview templates for specific collections or files, allowing for tailored preview experiences.
### Live Preview
Sveltia CMS does not plan to support WYSIWYG live site previews that fetch data from the actual website, due to its framework-agnostic design. If you require this feature, consider using a framework-specific CMS solution.
### User Settings
End-users can enable or disable the preview pane in the CMS UI using the menu located at the top-right corner of the editor interface. This preference is saved in the browser’s local storage, allowing users to maintain their preferred preview state across sessions.
Scroll syncing between the editor and preview panes is enabled by default. Users can toggle this feature on or off using the same editor menu.
---
---
url: /en/docs/ui/content-library.md
description: >-
Use Content Library in Sveltia CMS to browse, search, and manage all entries
and files.
---
# Content Library
Manage your entries and files in one place. The Content Library provides a centralized location to organize, search, and manage all your contents efficiently.
## Features
### Collection List
Displays all [collections](/en/docs/collections) with entry counts for quick access. [Singletons](/en/docs/collections/singletons) are marked distinctly to differentiate them from regular collections.
Customization options include:
* Collection labels can be defined using the `label` option in your collection configuration.
* Collection icons can be set using the [`icon` option](/en/docs/collections#icons) in your collection configuration.
* Dividers can be added between collections using the [`divider` option](/en/docs/collections#dividers) in your collection configuration.
### Entry List
To access the Entry List, navigate to the Content Library and select a collection from the Collection List. Entries within a selected collection are displayed in a user-friendly, customizable list.
#### Entry Summaries
To customize the information displayed for each entry in the Entry List, you can define a [summary field](/en/docs/collections/entries#summaries) in your entry collection configuration. This allows you to highlight specific fields that are most relevant to your workflow. For file collections, the filename is used as the summary.
#### View Modes
Users can switch between list and grid views for better visualization of entries. The grid view is especially useful when entries have associated images. If no Image field is present, only the list view is available.
#### Sorting, Filtering and Grouping
Entry collections can be sorted, filtered, and grouped based on various criteria to help users find specific entries quickly. Users can sort entries by fields such as date created, date modified, title, or any custom field defined in the collection. Filtering options allow users to narrow down entries based on specific field values, while grouping helps organize entries into categories for easier navigation.
See [Managing Entry Views](/en/docs/collections/entries#managing-entry-views) for how to configure these options.
#### Associated Assets
Assets stored in a [collection media folder](/en/docs/media/internal#collection-level-configuration) are displayed alongside their respective entries for easy identification.
#### Bulk Actions
Users can select multiple entries to delete them at once, streamlining content management tasks.
### Content Search
Instant full-text search across all entries and files helps you find content quickly. Search results are ranked by relevance to ensure you get the most pertinent results first.
---
---
url: /en/docs/workflows.md
description: Configure content management workflows in Sveltia CMS for your team’s needs.
---
# Content Management Workflows
Sveltia CMS supports several workflows to accommodate different content management needs. Below are the available workflows:
## Development
[Local Workflow](/en/docs/workflows/local) is available for development and testing purposes. It allows you to run Sveltia CMS without needing to connect to a remote repository or authentication service.
## Production
There are two main workflows for production use:
* [Simple Workflow](/en/docs/workflows/simple): no review process, editors can directly commit changes to the main branch.
* [Editorial Workflow](/en/docs/workflows/editorial): includes a review and approval process before changes are merged into the main branch.
Additionally, the following feature enhances the content management experience:
* [Open Authoring](/en/docs/workflows/open): allows external contributors to submit changes via pull requests.
These production workflows can be used locally or remotely. Not all workflows are supported by every backend; refer to the specific workflow documentation for details.
::: info Future Plans
We’re planning to introduce **Preview Workflow** in the future, which will allow editors to preview their changes before publishing them live. It would be a simplified version of Editorial Workflow, enabling content previews by creating a preview branch (pull/merge request) without a formal review process. Major hosting services like [Netlify](https://docs.netlify.com/deploy/deploy-types/deploy-previews/) and [Cloudflare Pages](https://developers.cloudflare.com/pages/configuration/preview-deployments/) support preview deployments from pull/merge requests, making this workflow feasible.
:::
---
---
url: /en/docs/content-modeling.md
description: >-
Design effective content models in Sveltia CMS with planning guides and best
practices.
---
# Content Modeling Guide
Sveltia CMS is a generic-purpose content management system that can be adapted to various use cases. How to structure your collections and design your content models depends on your project requirements. Here are some guidelines and examples to help you get started.
## Planning Your Content Model
Content modeling is primarily a non-technical task that requires understanding of your content and how it will be used. It’s recommended to involve content editors and stakeholders in the planning process to ensure the content model meets everyone’s needs. If you are building a site for a client, collaborate with them to understand their content requirements and workflows.
## Understanding Content Models
A content model defines the structure and organization of your content within Sveltia CMS. It consists of [collections](/en/docs/collections), which are groups of related content items, and [fields](/en/docs/fields), which define the properties of each content item.
When designing your content model, consider the following:
* **Content types**: Identify the different types of content you need to manage (e.g., blog posts, products, pages).
* **Collections**: Decide whether to use [entry collections](/en/docs/collections/entries) (for multiple similar items) or [file collections](/en/docs/collections/files) (for individual files) based on your content types.
* **Fields**: Define the fields required for each content type, including their data types and validation rules.
* **Relationships**: Determine if there are any relationships between different content types that need to be represented (e.g., authors for blog posts, categories for products).
## Tips for Designing Content Models
Some best practices for designing effective content models in Sveltia CMS include:
* **Plan ahead**: Take the time to think through your content structure before creating collections and fields. This will help avoid unnecessary changes later.
* **Keep it simple**: Start with a basic structure and expand as needed. Avoid overcomplicating your content model.
* **Use meaningful names**: Choose clear and descriptive names for collections and fields to make it easier to understand.
* **Leverage field types**: Utilize the [various field types](/en/docs/fields#field-types) available in Sveltia CMS to capture different kinds of data effectively.
* **Plan for scalability**: Consider how your content model may need to evolve over time and design it to accommodate future changes.
* **Test and iterate**: Regularly review and refine your content model based on feedback from content editors and users.
## Using Relations Between Collections
Sveltia CMS supports [Relation fields](/en/docs/fields/relation) that allow you to create relationships between different collections. This is useful for linking related content items, such as associating blog posts with authors or products with categories.
When using relations, consider the following:
* **Cardinality**: Decide whether the relationship is one-to-one, one-to-many, or many-to-many, and configure the Relation field accordingly.
* **Performance**: Be mindful of the potential performance implications of complex relationships, especially with large datasets.
* **User experience**: Ensure that the relationship is intuitive for content editors, providing clear labels and options in the CMS interface.
## Examples of Content Models
Here are some common content models for different types of websites and applications, along with suggestions on how to structure your collections and fields.
### Blog or News Site
* **Posts**: [entry collection](/en/docs/collections/entries) for blog posts or news articles, with fields: title (String, required), body (RichText, required), author (Relation), date (DateTime, required), tags (Relation), status (Select: draft/published/archived), featured\_image (Image), and excerpt (Text). Organized by date with draft/published workflow.
* **Tags**: entry collection for managing available tags, linked to posts via a Relation field. For more details, see our [how-to](/en/docs/how-tos#using-entry-tags-for-categorization) on this topic.
* **Authors**: entry collection for managing the list of authors, with fields: name (String, required), bio (RichText), photo (Image), and email (String), linked to posts via a Relation field.
* **Pages**: [file collection](/en/docs/collections/files) for static pages like About or Contact.
### Documentation Site
* **Documents**: [entry collection](/en/docs/collections/entries) for documentation pages, with fields: title (String, required), body (RichText, required), version (Relation), sidebar\_position (Number), published (Boolean), and search\_keywords (String, optional), linked to versions via a Relation field.
* **Versions**: entry collection for managing available versions (e.g., v1.0, v2.0), linked to documentation pages via a Relation field.
* **Categories**: entry collection for organizing documentation by category, with fields: name (String, required), slug (String, required), and order (Number), linked to documents via a Relation field.
* **Tutorials**: entry collection for step-by-step tutorials and guides, with fields: title (String, required), content (RichText, required), difficulty (Select: beginner/intermediate/advanced), estimated\_time (Number), and published (Boolean), linked to the Tutorials page via a Relation field.
* **Pages**: [file collection](/en/docs/collections/files) for static pages like Getting Started or FAQ.
### Portfolio Site
* **Portfolio items**: [entry collection](/en/docs/collections/entries) for portfolio items, with fields: title (String, required), description (RichText), images (List of Images), project\_url (String), categories (Relation), skills (Relation), year (Number), and status (Select: in\_progress/completed/archived), linked to categories and skills via Relation fields.
* **Categories**: entry collection for managing available categories (e.g., Web Design, Branding, Photography), linked to portfolio items via a Relation field.
* **Skills**: entry collection for managing the list of skills or technologies used (e.g., React, Figma, CSS), linked to portfolio items via a Relation field.
* **Testimonials**: entry collection for client testimonials, with fields: client\_name (String, required), testimonial\_text (Text, required), company (String), photo (Image), and rating (Select: 1-5 stars), linked to portfolio items via a Relation field.
* **Pages**: [file collection](/en/docs/collections/files) for static pages like About Me or Services.
### Corporate Website
* **Pages**: [file collection](/en/docs/collections/files) for static pages like Home, About Us, Services, and Contact.
* **Services**: [entry collection](/en/docs/collections/entries) for services offered, with fields: name (String, required), description (RichText, required), icon (Image), price (String), and featured (Boolean).
* **Products**: entry collection for products or offerings, with fields: name (String, required), brand (Relation), description (RichText), price (Number), image (Image), sku (String, required), and in\_stock (Boolean).
* **Brands**: entry collection for brand information, with fields: name (String, required), logo (Image), description (RichText), website (String), and featured (Boolean).
* **Clients**: entry collection for client logos or profiles, with fields: name (String, required), logo (Image), website (String), and contact\_person (String), linked to testimonials and case studies via Relation fields.
* **Testimonials**: entry collection for client testimonials, with fields: client (Relation, required), testimonial\_text (Text, required), contact\_person (String, required), photo (Image), and rating (Select: 1-5 stars), linked to clients via a Relation field.
* **Case studies**: entry collection for case studies, with fields: title (String, required), description (RichText), client (Relation, required), images (List of Images), results (RichText), and featured (Boolean), linked to clients via a Relation field.
* **Leadership**: entry collection for team members, with fields: name (String, required), role (String, required), bio (RichText), photo (Image), email (String), and social\_links (KeyValue).
* **Locations**: entry collection for office locations, with fields: name (String, required), address (String), city (String), country (String), phone (String), email (String), and map\_url (String).
* **Careers**: entry collection for job openings, with fields: job\_title (String, required), description (RichText), location (Relation), employment\_type (Select: full-time/part-time/contract), and application\_url (String).
* **News**: entry collection for news or press releases, with fields: title (String, required), content (RichText), date (DateTime), and published (Boolean).
### Personal Blog with Static Pages
* **Posts**: [entry collection](/en/docs/collections/entries) for blog posts, with fields: title (String, required), body (RichText, required), date (DateTime, required), tags (Relation), excerpt (Text), featured\_image (Image), published (Boolean), and reading\_time (Number, auto-calculated), linked to tags via a Relation field.
* **Tags**: entry collection for managing available tags, linked to posts via a Relation field. For more details, see our [how-to](/en/docs/how-tos#using-entry-tags-for-categorization) on this topic.
* **Projects**: entry collection for personal projects or side projects, with fields: title (String, required), description (RichText), images (List of Images), url (String), technology (String), and featured (Boolean).
* **Pages**: [file collection](/en/docs/collections/files) for static pages like About Me and Contact.
### Event Management Site
* **Events**: [entry collection](/en/docs/collections/entries) for events, with fields: name (String, required), date (DateTime, required), location (Relation), description (RichText), registration\_url (String), capacity (Number), status (Select: upcoming/ongoing/completed/cancelled), and featured (Boolean), linked to categories, locations, and sessions via Relation fields.
* **Categories**: entry collection for event categories (e.g., Conference, Webinar, Workshop), linked to events via a Relation field.
* **Locations**: entry collection for available venues, with fields: name (String, required), address (String), city (String), capacity (Number), and map\_url (String), linked to events via a Relation field.
* **Organizers**: entry collection for event organizers, with fields: name (String, required), role (String), bio (RichText), and photo (Image).
* **Speakers**: entry collection for event speakers, with fields: name (String, required), bio (RichText), photo (Image), and website (String).
* **Sessions**: entry collection for event sessions, with fields: title (String, required), speaker (Relation), time (DateTime), duration (Number), room (String), and description (RichText).
* **Sponsors**: entry collection for event sponsors, with fields: name (String, required), logo (Image), website (String), sponsorship\_level (Select: bronze/silver/gold/platinum), and featured (Boolean), linked to events via a Relation field.
* **Pages**: [file collection](/en/docs/collections/files) for static pages like Event Guidelines or FAQ.
### Music Band Website
* **Albums**: [entry collection](/en/docs/collections/entries) for albums, with fields: title (String, required), release\_date (DateTime), tracklist (Relation), cover\_image (Image), description (RichText), genre (Select), and streaming\_links (KeyValue), linked to tracks via a Relation field.
* **Tracks**: entry collection for tracks within each album, with fields: title (String, required), duration (Number), audio\_url (String), lyrics (RichText, optional), and featured\_artist (String), linked to albums via a Relation field.
* **Members**: entry collection for band members, with fields: name (String, required), role (String, required), bio (RichText), photo (Image), and social\_links (KeyValue).
* **Tour dates**: entry collection for tour dates, with fields: event\_name (String, required), date (DateTime, required), location (Relation), ticket\_url (String), and sold\_out (Boolean), linked to locations via a Relation field.
* **Locations**: entry collection for available venues, with fields: name (String, required), city (String), country (String), and capacity (Number), linked to tour dates via a Relation field.
* **Media**: entry collection for band media including photos and concert videos, with fields: title (String, required), type (Select: photo/video), files (List of Images or File), date (DateTime), event (String, optional), and featured (Boolean), organized by date with featured media highlighted on the gallery page.
* **Pages**: [file collection](/en/docs/collections/files) for static pages like About the Band or Contact.
### Recipe Website
* **Recipes**: [entry collection](/en/docs/collections/entries) for recipes, with fields: title (String, required), description (RichText), categories (Relation), ingredients (Relation), instructions (RichText, required), cooking\_time (Number), prep\_time (Number), servings (Number), difficulty (Select: easy/medium/hard), and images (List of Images), linked to categories, ingredients, and chefs via Relation fields.
* **Categories**: entry collection for recipe categories (e.g., Appetizers, Desserts, Main Courses), linked to recipes via a Relation field.
* **Ingredients**: entry collection for available ingredients, with fields: name (String, required), description (RichText, optional), image (Image), and unit (String), linked to recipes via a Relation field.
* **Chefs**: entry collection for chefs, with fields: name (String, required), bio (RichText), specialties (String), photo (Image), and website (String), linked to recipes via a Relation field.
* **Cooking tips**: entry collection for cooking tips, with fields: title (String, required), content (RichText, required), category (Select), and images (List of Images).
* **Pages**: [file collection](/en/docs/collections/files) for static pages like Cooking Tips or About the Chef.
### Educational Platform
* **Courses**: [entry collection](/en/docs/collections/entries) for courses, with fields: title (String, required), description (RichText, required), instructor (Relation, required), categories (Relation), location (Relation, optional), enrollment\_link (String), level (Select: beginner/intermediate/advanced), price (Number, optional), status (Select: draft/published/archived), linked to instructors, categories, lessons, and locations via Relation fields.
* **Instructors**: entry collection for instructors, with fields: name (String, required), bio (RichText), photo (Image), email (String), contact\_info (KeyValue), and expertise (String), linked to courses and blog posts via Relation fields.
* **Lessons**: entry collection for lessons within each course, with fields: title (String, required), course (Relation, required), content (RichText), resources (List of Strings), order (Number), and video\_url (String, optional), linked to courses via a Relation field.
* **Categories**: entry collection for course categories (e.g., Web Development, Design, Business), linked to courses via a Relation field.
* **Locations**: entry collection for available venues (for in-person courses), with fields: name (String), city (String), and address (String), linked to courses via a Relation field.
* **Testimonials**: entry collection for student testimonials, with fields: student\_name (String, required), course (Relation), testimonial\_text (Text, required), rating (Select: 1-5), and photo (Image, optional).
* **Events**: entry collection for upcoming educational events (workshops, webinars), with fields: title (String, required), date (DateTime, required), description (RichText), registration\_url (String), and capacity (Number).
* **Blog posts**: entry collection for educational articles and updates, with fields: title (String, required), body (RichText, required), author (Relation), date (DateTime, required), tags (Relation), published (Boolean), and featured\_image (Image), linked to instructors (as authors) and tags via Relation fields.
* **Tags**: entry collection for managing blog post tags, linked to blog posts via a Relation field.
* **Pages**: [file collection](/en/docs/collections/files) for static pages like About Us or Contact.
### Non-Profit Organization Site
* **Pages**: [file collection](/en/docs/collections/files) for static pages like Mission, Programs, Leadership, Donate, and Contact.
* **News**: [entry collection](/en/docs/collections/entries) for news related to the organization, with fields: title (String, required), content (RichText, required), date (DateTime, required), featured\_image (Image), author (String), and published (Boolean).
* **Events**: entry collection for upcoming events, with fields: name (String, required), date (DateTime, required), location (Relation), description (RichText), registration\_url (String), and featured (Boolean).
* **Locations**: entry collection for available venues, with fields: name (String, required), address (String), city (String), and capacity (Number), linked to events via a Relation field.
* **Board Members**: entry collection for board members, with fields: name (String, required), role (String, required), bio (RichText), photo (Image), and email (String), linked to the Leadership page via a Relation field.
* **Programs**: entry collection for programs offered by the organization, with fields: name (String, required), description (RichText, required), goals (List of Strings), images (List of Images), budget (Number, optional), and impact (RichText), linked to the Programs page via a Relation field.
* **Volunteer Opportunities**: entry collection for volunteer roles, with fields: title (String, required), description (RichText), requirements (List of Strings), commitment (Select: flexible/regular/one-time), and application\_url (String).
* **Success Stories**: entry collection for success stories, with fields: title (String, required), content (RichText, required), featured\_image (Image), author (String), date (DateTime), and impact\_metric (String).
* **Partners**: entry collection for partner organizations, with fields: name (String, required), description (RichText, optional), logo (Image), website (String), and partnership\_type (String).
* **Sponsors**: entry collection for sponsors, with fields: name (String, required), description (RichText, optional), logo (Image), website (String), and sponsorship\_level (Select: bronze/silver/gold/platinum).
* **Campaigns**: entry collection for fundraising campaigns, with fields: title (String, required), description (RichText, required), goal\_amount (Number), raised\_amount (Number, auto-updated), end\_date (DateTime, required), images (List of Images), and status (Select: active/completed/paused).
## Real-World Examples
To see how others have structured their content models using Sveltia CMS, check out our [Showcase](/en/showcase) page. It features a variety of websites using Sveltia CMS. Most of them include a link to their source code, which can provide valuable insights into different content modeling approaches.
---
---
url: /en/docs/api/editor-components.md
description: >-
Create custom editor components in Sveltia CMS for reusable rich text editor
blocks.
---
# Custom Editor Components
A custom editor component allows you to create reusable, complex block-level component available in the [rich text editor](/en/docs/fields/richtext). Registered components appear under the Insert button on the editor toolbar. When clicked, they insert a predefined template into the editor at the current cursor position.
## Overview
To register a custom editor component, use the `registerEditorComponent` method on the [`CMS` object](/en/docs/api#accessing-the-cms-object):
```js
CMS.registerEditorComponent(definition);
```
The component `definition` object includes the following properties:
### Required Properties
* `id` (string): A unique identifier for the component. This is the name you will use to reference this component in the `editor_components` option for a [RichText](/en/docs/fields/richtext) or [Markdown](/en/docs/fields/markdown) field. It should be unique and not conflict with built-in component IDs (`code-block`, `image`).
* `fields` (array of field definitions): An array defining the [fields](/en/docs/fields) to be displayed in the component.
* `pattern` (RegExp): A regular expression used to identify existing instances of the component in the Markdown content.
* It’s recommended to use [named capture groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group) corresponding to the field names so that `fromBlock` can be omitted if no additional processing is needed.
* Matching could be either block (multiline) or inline, depending on the component. To match block content, use the `s` (dotAll) or `m` (multiline) flag, or include `[\s\S]` in the pattern.
* `fromBlock` (function): A function that takes a regex match array and returns an object mapping field names to their values.
* This property can be omitted if the `pattern` regular expression contains named capture groups corresponding to the field names, and no additional processing like type conversion is needed.
* Otherwise, this property is required. You must provide a function to extract field values from the regex match.
* `toBlock` (function): A function that takes an object mapping field names to their values and returns a string representing the Markdown content to be inserted.
### Optional Properties
* `label` (string): The text label displayed on the toolbar button. Defaults to the `id` value.
* `icon` (string): A [Material Symbols](https://fonts.google.com/icons?icon.set=Material+Symbols) icon name to display on the toolbar button.
* `toPreview` (function): A function that takes an object mapping field names to their values and returns a string or React **class component** representing the HTML preview of the component in the editor. If omitted, no preview is shown.
* `collapsed` (boolean): If true, the component’s fields panel is collapsed by default when the component is inserted.
::: warning Unimplemented
`toPreview` is not yet supported in Sveltia CMS. It will be supported soon.
:::
## Using Components
Once registered, custom editor components can be used in any [RichText](/en/docs/fields/richtext) or [Markdown](/en/docs/fields/markdown) field. By default, all built-in and custom components are included. You can restrict which components are available by adding their `id` to the field’s `editor_components` array in the collection configuration.
For example, to allow only the built-in `image` component and custom `callout` and `youtube` components:
::: code-group
```yaml{5} [YAML]
fields:
- name: content
label: Content
widget: richtext
editor_components: [image, callout, youtube]
```
```toml{5} [TOML]
[[fields]]
name = "content"
label = "Content"
widget = "richtext"
editor_components = ["image", "callout", "youtube"]
```
```json{7} [JSON]
{
"fields": [
{
"name": "content",
"label": "Content",
"widget": "richtext",
"editor_components": ["image", "callout", "youtube"]
}
]
}
```
```js{7} [JavaScript]
{
fields: [
{
name: "content",
label: "Content",
widget: "richtext",
editor_components: ["image", "callout", "youtube"],
},
],
}
```
:::
## Examples
### Callout
The following example demonstrates how to register a custom editor component for a "Callout" block:
```js
CMS.registerEditorComponent({
id: 'callout',
label: 'Callout',
icon: 'campaign',
fields: [
{ name: 'type', label: 'Type', widget: 'select', options: ['info', 'warning', 'error'] },
{ name: 'message', label: 'Message' },
],
pattern: /^:::callout (\w+)\n([\s\S]+?)\n:::/m,
fromBlock: (match) => ({
type: match[1],
message: match[2].trim(),
}),
toBlock: (data) => `:::callout ${data.type}\n${data.message}\n:::`,
toPreview: (data) => `:::callout ${data.type}\n${data.message}\n:::`,
});
```
In this example, the “Callout” component allows users to insert a callout block with a specified type (info, warning, or error) and a message. The `pattern` regular expression is used to identify existing callout blocks in the Markdown content, while the `fromBlock` and `toBlock` functions handle the conversion between the component's data and its Markdown representation.
### File Link
This example demonstrates how to create a custom editor component for inserting a file link using the built-in [`file` field type](/en/docs/fields/file):
```js
CMS.registerEditorComponent({
id: 'file-link',
label: 'File Link',
icon: 'attach_file',
fields: [
{ name: 'file', label: 'File', widget: 'file' },
{ name: 'text', label: 'Text to Display', default: '{{file}}' },
],
pattern: /([^\n]+?)<\/a>/,
fromBlock: (match) => ({
file: decodeURI(match[1]),
text: match[2],
}),
toBlock: (data) => `${data.text}`,
toPreview: (data) => `${data.text}`,
});
```
### Collapsible Note
Here’s an example of a collapsible “Note” component:
```js
CMS.registerEditorComponent({
id: 'note',
label: 'Note',
icon: 'note_alt',
fields: [
{ name: 'summary', label: 'Summary' },
{ name: 'content', label: 'Content', widget: 'richtext' },
],
pattern: /^\s*(?.+?)<\/summary>\s*(?[\s\S]+?)\s*<\/details>/m,
toBlock: ({ summary, content }) =>
`\n${summary}\n${content}\n`,
toPreview: ({ summary, content }) =>
`\n${summary}\n
${content}
\n`,
});
```
In this example, the “Note” component creates a collapsible section using HTML `` and `` tags. The `fromBlock` function is omitted because the `pattern` regular expression uses named capture groups that correspond to the field names. The `toBlock` function generates the appropriate HTML structure for the note component based on the provided summary and content.
### YouTube Embed with Hugo Shortcode
```js
CMS.registerEditorComponent({
id: 'youtube',
label: 'YouTube',
icon: 'youtube_activity',
fields: [
{ name: 'id', label: 'ID' },
{ name: 'width', label: 'Width', widget: 'number', valueType: 'int', default: 560 },
{ name: 'height', label: 'Height', widget: 'number', valueType: 'int', default: 315 },
],
pattern: /{{< youtube id="(?.*?)"(?: width="(?.*?)" height="(?.*?)")? >}}/m,
fromBlock: ({ groups: { id, width, height } = {} }) => ({
id,
width: width ? Number(width) : 560,
height: height ? Number(height) : 315,
}),
toBlock: ({ id, width = 560, height = 315 }) =>
`{{< youtube id="${id}" width="${width}" height="${height}" >}}`,
toPreview: ({ id, width = 560, height = 315 }) =>
id
? ``
: '',
});
```
In this example, the “YouTube” component allows users to embed YouTube videos using a Hugo shortcode. The `pattern` regular expression captures the video ID, width, and height from the shortcode. The `fromBlock` function processes the captured values, casting width and height to numbers. The `toBlock` function generates the shortcode string, while the `toPreview` function creates an iframe preview of the embedded video.
The `pattern` uses the `m` (multiline) flag to make the component block-level, though it’s not multiline in this case.
### Using React for Preview
---
---
url: /en/docs/api/field-types.md
description: >-
Create custom field types in Sveltia CMS for reusable, complex input controls
and previews.
---
# Custom Field Types
A custom field type allows you to create reusable, complex input controls and previews available in the CMS interface. Registered field types can be used in your collection just like [built-in field types](/en/docs/fields#built-in-field-types).
::: warning Unimplemented
This feature is not yet supported in Sveltia CMS. It will be added soon.
:::
::: tip Note for Netlify/Decap CMS Users
In Sveltia CMS, what was previously referred to as a **widget** in Netlify/Decap CMS is now called a **field type**. This change was made to better align with common content management terminology, as originally [proposed](https://github.com/decaporg/decap-cms/issues/3719) by Netlify CMS maintainers themselves.
The `registerWidget` method from Netlify/Decap CMS has been renamed to `registerFieldType` in Sveltia CMS to reflect this terminology change, but the old name remains available as an alias for backward compatibility. The signature and behavior are identical.
:::
## Overview
To register a custom field type, use the `registerFieldType` method on the [`CMS` object](/en/docs/api#accessing-the-cms-object):
```js
CMS.registerFieldType(name, control, [preview], [schema]);
```
For backward compatibility with Netlify/Decap CMS, the `registerWidget` method is available as an alias with the same signature.
### Parameters
* `name` (string, required): The name of the custom field type. This is the name you will use in your collection configuration to reference this type. It should be unique and not conflict with [built-in field types](/en/docs/fields#built-in-field-types) names.
* `control` (React component, required): A React **class component** that defines the control (input) part of the field.
* `preview` (React component, optional): A React **class component** that defines how the field’s value is previewed in the CMS preview pane. If not provided, no preview will be shown.
* `schema` (object, optional): A [JSON schema](https://json-schema.org/) object that defines the configuration options for the field type.
---
---
url: /en/docs/api/file-formats.md
description: >-
Register custom file format parsers and formatters in Sveltia CMS for
non-standard file types.
---
# Custom File Formats
Sveltia CMS comes with built-in support for common file formats like JSON, YAML, TOML and Markdown. However, you can also register your own custom parsers and formatters to handle different file types or formats.
## Overview
To register a custom file format, use the `registerCustomFormat` method on the [`CMS` object](/en/docs/api#accessing-the-cms-object):
```js
CMS.registerCustomFormat(name, extension, { fromFile, toFile });
```
### Parameters
* `name` (string): A unique name for the custom format. This name will be used to reference the format in collection configurations.
* `extension` (string): The file extension associated with this format (e.g., `json5`, `yaml`, `toml`).
* `fromFile` (function): A parser function that takes a string (the content of the file) and returns a JavaScript object.
* `toFile` (function): A formatter function that takes a JavaScript object and returns a string (the content to be saved to the file).
You can omit either `fromFile` or `toFile` if you only need to customize one direction (parsing or formatting). If you omit `fromFile`, the CMS will use the default parser for the specified file extension. Similarly, if you omit `toFile`, the CMS will use the default formatter. You must provide at least one of the two functions.
The functions `fromFile` and `toFile` can also be asynchronous, allowing you to perform async operations if needed.
## Using Custom Formats
Once registered, the custom format can be used in your collection configurations by specifying the `format` property. For example:
::: code-group
```yaml{3} [YAML]
collections:
- name: myCollection
format: json5
fields:
- name: item1
label: Item 1
```
```toml{3} [TOML]
[[collections]]
name = "myCollection"
format = "json5"
[[collections.fields]]
name = "item1"
label = "Item 1"
```
```json{5} [JSON]
{
"collections": [
{
"name": "myCollection",
"format": "json5",
"fields": [
{
"name": "item1",
"label": "Item 1"
}
]
}
]
}
```
```js{5} [JavaScript]
{
collections: [
{
name: "myCollection",
format: "json5",
fields: [
{
name: "item1",
label: "Item 1",
},
],
},
],
}
```
:::
You don’t need to specify the file `extension` in the collection configuration; the CMS will automatically use the correct extension based on the registered format.
## Examples
### Registering JSON5 Format
The following example demonstrates how to register a custom formatter for the JSON5 format using the `json5` library:
```js
import JSON5 from 'json5';
CMS.registerCustomFormat('json5', 'json5', {
fromFile: (text) => JSON5.parse(text),
toFile: (data) => JSON5.stringify(data, null, 2),
});
```
### Registering YAML Parser with Custom Behavior
You can customize the behavior of the formatter functions. For example, you might want to add a timestamp to the YAML file whenever it is saved:
```js
import YAML from 'js-yaml';
CMS.registerCustomFormat('yaml', 'yaml', {
fromFile: (text) => YAML.load(text),
toFile: (data) => YAML.dump({ ...data, last_updated: new Date().toISOString() }),
});
```
An [event hook](/en/docs/api/events) is a better way to add metadata like timestamps, but this example illustrates how you can customize the formatter functions.
### Registering JavaScript Module Format
The following example demonstrates how to register a custom formatter for JavaScript modules that export data using `export default` syntax and parse it back into a JavaScript object:
```js
CMS.registerCustomFormat('mjs', 'js', {
fromFile: (text) => JSON.parse(text.replace(/^export default (.+);$/s, '$1')),
toFile: (data) => `export default ${JSON.stringify(data, null, 2)};`,
});
```
The file extension is set to `js` for demonstration purposes. You can use `mjs` if you prefer.
This example uses a simple regex to extract the JSON object from the `export default` statement. If you need a more robust solution for parsing JavaScript modules, consider using a library like `acorn` or `esbuild` to handle the parsing.
### Registering an Asynchronous Formatter
The parser and formatter functions can also be asynchronous. For example, you might want to format JSON data using a library like Prettier, which returns a promise:
```js
import Prettier from 'prettier';
CMS.registerCustomFormat('json', 'json', {
toFile: async (data) => Prettier.format(data),
});
```
If you omit `fromFile`, the CMS will fall back to the default parser for that file extension. In this case, the standard `JSON.parse` method will be used to parse JSON files.
---
---
url: /en/docs/api/preview-styles.md
description: >-
Customize preview pane styles in Sveltia CMS with CSS to match your site’s
design.
---
# Custom Preview Styles
Sveltia CMS comes with built-in preview styles for the admin interface. However, you can also register your own custom preview styles to customize the appearance of the admin interface according to your needs.
## Overview
To register a custom preview style, use the `registerPreviewStyle` method on the [`CMS` object](/en/docs/api#accessing-the-cms-object):
```js
CMS.registerPreviewStyle(filePath);
```
```js
CMS.registerPreviewStyle(cssString, { raw: true });
```
There are two ways to register custom preview styles in Sveltia CMS: by providing a file path to a CSS file or by providing a raw CSS string. If you provide a raw CSS string, you need to set the `raw` option to `true`.
### Parameters
* `filePath` (string): The path to the CSS file containing the custom styles. This file should be accessible from the CMS admin interface. It can be a relative path or an absolute URL.
* `cssString` (string): A string containing the raw CSS styles to be applied to the admin interface.
* `options` (object, optional): An options object that can contain the following property:
* `raw` (boolean): Set this to `true` if you are providing a raw CSS string. Defaults to `false`.
## Examples
### Registering a Preview Style from a File
To register a preview style from a CSS file, simply provide the file path as an argument to the `registerPreviewStyle` function.
```js
CMS.registerPreviewStyle('/path/to/your/custom-style.css');
```
### Registering a Preview Style from a Raw CSS String
You can also register a preview style by providing a raw CSS string. Make sure to set the `raw` option to `true` in the options object.
```js [JavaScript]
const customCSS = `
.nc-admin {
background-color: #f0f0f0;
}
`;
CMS.registerPreviewStyle(customCSS, { raw: true });
```
### Registering Multiple Preview Styles
You can register multiple preview styles by calling the `registerPreviewStyle` function multiple times. The styles will be applied in the order they were registered.
```js
CMS.registerPreviewStyle('/path/to/first-style.css');
CMS.registerPreviewStyle('/path/to/second-style.css');
```
This allows you to layer styles and create complex customizations for the Sveltia CMS admin interface.
---
---
url: /en/docs/api/preview-templates.md
description: >-
Define custom preview templates in Sveltia CMS to control how content entries
are displayed.
---
# Custom Preview Templates
A custom preview template allows you to define how content entries are displayed in the CMS preview pane. By registering a custom preview template, you can create a more tailored and user-friendly editing experience for content editors.
::: warning Unimplemented
This feature is not yet supported in Sveltia CMS. It will be added soon.
:::
## Overview
To register a custom preview template, use the `registerPreviewTemplate` method on the [`CMS` object](/en/docs/api#accessing-the-cms-object):
```js
CMS.registerPreviewTemplate(name, component);
```
### Parameters
* `name` (string, required): The name of the collection or collection file for which the preview template is being registered.
* `component` (React component, required): A React **class component** that defines the preview template. This component receives the entry data as props and should render the preview accordingly.
---
---
url: /en/docs/customization.md
description: >-
Customize Sveltia CMS with custom branding, mounting options, JavaScript API,
and source modifications.
---
# Customization
Sveltia CMS offers various customization options to tailor the admin interface and functionality to your specific needs. This guide provides an overview of the available customization features in Sveltia CMS.
## Site URL
The `site_url` configuration option allows you to specify the base URL of your Sveltia CMS installation. This is useful for generating absolute URLs for assets, links, and redirects within the admin interface.
::: code-group
```yaml [YAML]
site_url: https://example.com
```
```toml [TOML]
site_url = "https://example.com"
```
```json [JSON]
{
"site_url": "https://example.com"
}
```
```js [JavaScript]
{
site_url: 'https://example.com',
}
```
:::
## Logout Redirect URL
The `logout_redirect_url` configuration option allows you to specify a custom URL to which users will be redirected after they log out of the Sveltia CMS admin interface. This can be useful for directing users back to your main website or a specific landing page.
::: code-group
```yaml [YAML]
logout_redirect_url: https://example.com/logged-out
```
```toml [TOML]
logout_redirect_url = "https://example.com/logged-out"
```
```json [JSON]
{
"logout_redirect_url": "https://example.com/logged-out"
}
```
```js [JavaScript]
{
logout_redirect_url: 'https://example.com/logged-out',
}
```
:::
## Custom Logo
You can customize the logo displayed in the Sveltia CMS admin interface by specifying a custom logo URL in the configuration file. This allows you to replace the default Sveltia CMS logo with your own branding.
The logo will appear in the header of the admin interface, login page, and the browser tab (favicon).
The `logo` configuration option, defined at the root level of the configuration file, accepts an object with the following properties:
* `src`: The URL or path to the custom logo image. (Required)
* `show_in_header`: A boolean indicating whether to display the logo in the header. (Optional, default: `true`)
Configuration example:
::: code-group
```yaml [YAML]
logo:
src: /path/to/your/logo.png
show_in_header: true
```
```toml [TOML]
[logo]
src = "/path/to/your/logo.png"
show_in_header = true
```
```json [JSON]
{
"logo": {
"src": "/path/to/your/logo.png",
"show_in_header": true
}
}
```
```js [JavaScript]
{
logo: {
src: '/path/to/your/logo.png',
show_in_header: true,
},
}
```
:::
For backward compatibility, the `logo_url` configuration option is still supported but deprecated. It is recommended to use the `logo` object for better flexibility and future-proofing.
::: code-group
```yaml [YAML]
logo_url: /path/to/your/logo.png
```
```toml [TOML]
logo_url = "/path/to/your/logo.png"
```
```json [JSON]
{
"logo_url": "/path/to/your/logo.png"
}
```
```js [JavaScript]
{
logo_url: '/path/to/your/logo.png',
}
```
:::
## Custom Application Title
With the `app_title` configuration option, you can set a custom title for the Sveltia CMS admin interface. This title will be displayed on the login page and in the browser tab. You may want to replace the default “Sveltia CMS” title with your company name or a specific title that reflects the purpose of the admin interface.
::: code-group
```yaml [YAML]
app_title: Acme Inc. Site Admin
```
```toml [TOML]
app_title = "Acme Inc. Site Admin"
```
```json [JSON]
{
"app_title": "Acme Inc. Site Admin"
}
```
```js [JavaScript]
{
app_title: 'Acme Inc. Site Admin',
}
```
:::
Note that this is not a white-label solution, so the name of Sveltia CMS will remain visible in some places. When a custom title is set, a small ”Powered by Sveltia CMS” label will appear in the footer of the login page.
## Custom Mount Element
Sveltia CMS mounts the admin interface to the `` element by default. However, you can specify a custom mount element by adding a `
` with a specific ID in your HTML. This way, you can embed the CMS admin interface within a specific section of your webpage, allowing to have a navigation bar or other content alongside the CMS.
The ID of the custom mount element is `nc-root`.
```html
```
Make sure to properly style the custom mount element to ensure the CMS interface displays correctly within your layout. You may need to set dimensions, overflow properties, or other CSS styles depending on your design requirements. Otherwise, the admin interface may not render as expected.
Sveltia CMS will automatically detect the presence of the `nc-root` element and mount the admin interface there instead of the default `` element.
::: tip
`nc-root` is short for “Netlify CMS Root,” a naming convention carried over from Netlify/Decap CMS to maintain familiarity for users transitioning between the two systems.
:::
## JavaScript API
Sveltia CMS offers a comprehensive API that enables developers to extend and customize its features. You can register custom field types, preview templates, editor components, and more to enhance the content management experience.
For detailed information on how to use the API, please refer to the [JavaScript API guide](/en/docs/api).
## Modifying Source Code
Sveltia CMS is an open-source project licensed under the [MIT License](https://choosealicense.com/licenses/mit/), and its source code is available on [GitHub](https://github.com/sveltia/sveltia-cms). Advanced users and developers can modify the source code to implement custom features or changes that are not available through the standard customization options.
However, please note that our source code is under active development with significant refactoring and improvements happening regularly. Direct modifications to the source code may lead to compatibility issues with future updates.
---
---
url: /en/docs/data-output.md
description: >-
Control data output formats in Sveltia CMS with customization options and best
practices.
---
# Data Output
Content in Sveltia CMS is stored in static files, which can be in various formats such as Markdown, YAML, TOML, or JSON. The format used for storing content can be configured on a per-collection basis.
In most cases, you don’t need to worry about the details of data output, as Sveltia CMS and consuming tools, like your frameworks, static site generators (SSGs) or parser libraries, handle it seamlessly. However, understanding how data is output can help you avoid unexpected issues, especially when using strict type validations or manually editing data files.
## File Formats
### Standard Formats
Sveltia CMS supports the following data output formats for content files out of the box:
* Markdown with YAML, TOML or JSON front matter
* YAML
* TOML
* JSON
* Raw text files, such as plain text, JSON, XML, or CSV files
To customize the format for each collection, see the [Entry Collection](/en/docs/collections/entries#file-format-and-extension) and [File Collection](/en/docs/collections/files#file-format-and-extension) documentation.
### Custom Formats
Sveltia CMS allows you to define custom data output formats using the API. You can create your own format handlers to meet specific requirements for your project. For more information on how to create custom formats, please refer to the [Custom File Formats API reference](/en/docs/api/file-formats).
## Data Output Conventions
Here are some key aspects of data output in Sveltia CMS:
### General Conventions
* **Field Ordering**: Fields are always saved in the order they are defined in the configuration, with key-value pairs, making Git commits clean and consistent. Some [exceptions](#understanding-exceptions) apply.
* **Time Formatting**: A standard time is formatted as `HH:mm:ss` instead of `HH:mm` for better framework compatibility.
* **File Formatting**: Line breaks are LF (`\n`) across all formats. A newline is added at the end of the file to prevent unnecessary changes.
* **Text Processing**: Leading and trailing whitespaces in text-type field values are automatically removed when you save an entry. No configuration option is required for this behavior.
* **Complete and Consistent Data Output**: Sveltia CMS saves proper values for all fields, such as an empty string, an empty array, or `null`, instead of omitting them. This differs from Netlify/Decap CMS, which often omits optional and empty fields.
* `required: false` makes data input optional, but doesn't make data output optional.
* To omit empty optional fields from data output, use `omit_empty_optional_fields: true` in the [output options](#controlling-data-output). This is useful if you have data type validations that expect `undefined`.
### Format-Specific Conventions
* **YAML String Folding**: YAML string folding (maximum line width) is disabled for improved framework compatibility.
* **Indentation**: Indentation is 2 spaces for YAML and JSON by default (configurable).
* **Quoting in YAML**: Strings in YAML can be unquoted, single-quoted, or double-quoted (configurable).
* **JSON and YAML Formatting Options**: Fully configurable in the [output options](#controlling-data-output).
* **TOML DateTime Values**: DateTime field values in ISO 8601 format are stored in native date/time format instead of quoted strings.
### Markdown Syntax
Due to the underlying [Lexical framework](https://lexical.dev/) used in Sveltia CMS, the following Markdown conventions are applied to the output of the [rich text editor](/en/docs/fields/richtext):
* **Indentation**: 4 spaces for nested lists and code blocks instead of 2 spaces.
* **Unordered List Markers**: Hyphens (`-`) are used for unordered list markers instead of asterisks (`*`).
* **Bold Text**: Double asterisks (`**`) are used for bold text instead of double underscores (`__`).
* **Italic Text**: Underscores (`_`) are used for italics instead of asterisks (`*`).
* **Horizontal Rules**: Three asterisks (`***`) are used for horizontal rules instead of three hyphens (`---`).
* **Line Breaks**: Soft line breaks (single line breaks) are used instead of hard line breaks (two or more spaces, escaped line breaks `\`, or HTML ` ` tags). In your framework, you may need to [enable the appropriate option](/en/docs/how-tos#rendering-soft-line-breaks-as-hard-line-breaks-in-markdown) to render soft line breaks as hard line breaks.
## Controlling Data Output
Sveltia CMS supports some data output options, including JSON/YAML formatting preferences, at the root level of the configuration file. The default options are listed below:
::: code-group
```yaml [YAML]
output:
omit_empty_optional_fields: false
encode_file_path: false # true to URL-encode file paths for File/Image fields
json:
indent_style: space # or tab
indent_size: 2
yaml:
quote: none # or single or double
indent_size: 2
indent_sequences: true # false for compact style
```
```toml [TOML]
[output]
omit_empty_optional_fields = false
encode_file_path = false # true to URL-encode file paths for File/Image fields
[output.json]
indent_style = "space" # or "tab"
indent_size = 2
[output.yaml]
quote = "none" # or "single" or "double"
indent_size = 2
indent_sequences = true # false for compact style
```
```json [JSON]
{
"output": {
"omit_empty_optional_fields": false,
"encode_file_path": false,
"json": {
"indent_style": "space",
"indent_size": 2
},
"yaml": {
"quote": "none",
"indent_size": 2,
"indent_sequences": true
}
}
}
```
```js [JavaScript]
{
output: {
omit_empty_optional_fields: false,
encode_file_path: false,
json: {
indent_style: 'space',
indent_size: 2,
},
yaml: {
quote: 'none',
indent_size: 2,
indent_sequences: true,
},
},
}
```
:::
## Understanding Exceptions
Content is generally saved as key-value pairs in a file, where the key is the field name and the value is the field value. However, there are some exceptions you should be aware of.
### The `body` Field
If the format is front matter, the `body` field is saved outside of the front matter block:
```yaml
---
title: My Post
date: 2025-01-01
---
This is the body of my post.
```
instead of
```yaml
---
title: My Post
date: 2025-01-01
body: This is the body of my post.
---
```
If there is only the `body` field, the front matter block is omitted altogether:
```yaml
This is the body of my post.
```
However, this doesn’t apply when i18n is enabled with the `single_file` structure. In this case, the `body` field is saved as part of key-value pairs under each locale in the front matter block:
```yaml
---
en:
title: My Post
date: 2025-01-01
body: This is the body of my post.
fr:
title: Mon article
date: 2025-01-01
body: C’est le corps de mon article.
---
```
### List Widget
There are two exceptional cases for the List widget:
#### `field` vs. `fields` Option
When the `fields` (plural) option is used, each item is saved as an object with key-value pairs. For example, the following configuration:
::: code-group
```yaml{4-5} [YAML]
- name: images
label: Images
widget: list
fields:
- { name: image, label: Image, widget: image }
```
```toml{5-8} [TOML]
[[fields]]
name = "images"
label = "Images"
widget = "list"
[[fields.fields]]
name = "image"
label = "Image"
widget = "image"
```
```json{7-9} [JSON]
{
"fields": [
{
"name": "images",
"label": "Images",
"widget": "list",
"fields": [
{ "name": "image", "label": "Image", "widget": "image" }
]
}
]
}
```
```js{7-9} [JavaScript]
{
fields: [
{
name: "images",
label: "Images",
widget: "list",
fields: [
{ name: "image", label: "Image", widget: "image" },
],
},
],
}
```
:::
will produce the output:
::: code-group
```yaml [YAML]
images:
- image: https://example.com/image1.jpg
- image: https://example.com/image2.jpg
```
```toml [TOML]
[[images]]
image = "https://example.com/image1.jpg"
[[images]]
image = "https://example.com/image2.jpg"
```
```json [JSON]
{
"images": [
{ "image": "https://example.com/image1.jpg" },
{ "image": "https://example.com/image2.jpg" }
]
}
```
:::
On the other hand, when the `field` (singular) option is used, the `name` property is omitted from the output.
::: code-group
```yaml{4} [YAML]
- name: images
label: Images
widget: list
field: { name: image, label: Image, widget: image }
```
```toml{5} [TOML]
[[fields]]
name = "images"
label = "Images"
widget = "list"
[fields.field]
name = "image"
label = "Image"
widget = "image"
```
```json{7} [JSON]
{
"fields": [
{
"name": "images",
"label": "Images",
"widget": "list",
"field": { "name": "image", "label": "Image", "widget": "image" }
}
]
}
```
```js{7} [JavaScript]
{
fields: [
{
name: "images",
label: "Images",
widget: "list",
field: { name: "image", label: "Image", widget: "image" },
},
],
}
```
:::
The output will be:
::: code-group
```yaml [YAML]
images:
- https://example.com/image1.jpg
- https://example.com/image2.jpg
```
```toml [TOML]
images = ["https://example.com/image1.jpg", "https://example.com/image2.jpg"]
```
```json [JSON]
{
"images": ["https://example.com/image1.jpg", "https://example.com/image2.jpg"]
}
```
:::
#### `root` Option
When the [`root` option](/en/docs/fields/list#top-level-list) is set to `true`, the List field is saved as a top-level list without a field name:
::: code-group
```yaml [YAML]
- name: John Doe
id: 12345
- name: Jane Smith
id: 67890
```
```json [JSON]
[
{ "name": "John Doe", "id": 12345 },
{ "name": "Jane Smith", "id": 67890 }
]
```
:::
instead of
::: code-group
```yaml [YAML]
members:
- name: John Doe
id: 12345
- name: Jane Smith
id: 67890
```
```json [JSON]
{
"members": [
{ "name": "John Doe", "id": 12345 },
{ "name": "Jane Smith", "id": 67890 }
]
}
```
:::
This `root` option doesn’t work with TOML format, as TOML doesn’t support top-level arrays.
---
---
url: /en/docs/fields/datetime.md
description: Select dates and times in Sveltia CMS with an interactive date/time picker.
---
# DateTime Field
The DateTime field type allows users to select and input dates and times using a date/time picker interface.
## User Interface
### Editor
The browser’s native date/time picker. Depending on the configuration, it can handle date-only, time-only, or both date and time inputs.
::: info Future Plans
We plan to enhance the UI with a custom date/time picker in the future.
:::
### Preview
A string representation of the date and/or time, formatted according to the specified `format`, `date_format`, or `time_format` options, or in ISO 8601 format by default.
## Data Type
A string representing the date and/or time in ISO 8601 format by default, or in a custom format if specified. The possible formats are:
* Date-only: `YYYY-MM-DD` (e.g., `2025-08-15`)
* Time-only:
* With `picker_utc`: `HH:mm:ss.SSSZ` (e.g., `14:30:00.000Z`).
* Without `picker_utc`: `HH:mm:ss` (e.g., `14:30:00`).
* Date and time:
* With `picker_utc`: `YYYY-MM-DDTHH:mm:ss.SSSZ` (e.g., `2025-08-15T14:30:00.000Z`).
* Without `picker_utc`: `YYYY-MM-DDTHH:mm:ss` (e.g., `2025-08-15T14:30:00`).
If the `format`, `date_format` or `time_format` option is specified, the string will follow the custom Day.js format defined.
If the `required` option is set to `false` and the field is left empty, the value will be an empty string.
If the output format is TOML, the date-time string will be represented as a native, unquoted TOML date value, time value, or date-time value, depending on the configuration, unless a custom `format` is specified.
## Data Validation
* If the `required` option is set to `true`, the date/time value must not be an empty string.
* The date/time value must be a valid date/time string according to the specified format or ISO 8601.
* If the `pattern` option is provided, the date/time value must match the specified regular expression pattern.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the DateTime field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `datetime`.
### Optional Options
::: warning Breaking changes from Netlify/Decap CMS
Sveltia CMS does not support the deprecated camelCase `dateFormat`, `timeFormat` and `pickerUtc` options. Use `date_format`, `time_format` and `picker_utc` instead.
Also, Sveltia CMS (and Decap CMS 3.1.1) has replaced the Moment.js library with Day.js for date formatting and parsing. Since [Day.js tokens](https://day.js.org/docs/en/display/format) are not 100% compatible with [Moment.js tokens](https://momentjs.com/docs/#/displaying/format/), this could be a breaking change in certain cases. Check your `format`, `date_format` and `time_format` options if you’re migrating from Netlify CMS or earlier versions of Decap CMS.
:::
::: info Future Plans
The `date_format` and `time_format` options derived from Netlify/Decap CMS may be confusing. We plan to add the new `type` option for selecting between date, time, and date-time inputs more intuitively, and introduce new input types: year, month and week.
:::
#### `default`
* **Type**: `string`
* **Default**: `""`
A default date and/or time value for the field in ISO 8601 format or the specified custom format. Use `{{now}}` to set the default value to the current date and time.
#### `picker_utc`
* **Type**: `boolean`
* **Default**: `false`
Determines whether the date/time picker uses UTC time or the user’s local time zone. This is particularly useful when using date-only input (`time_format: false`); without UTC, the stored date may shift depending on the user’s time zone.
If set to `false` (default), the picker will use the local time zone of the user. If the format is date/time or time-only, the stored value will not include the `Z` suffix or any time zone information.
If set to `true`, the date/time picker will use UTC time instead of the local time zone. If the format is date/time or time-only, the stored value will include the `Z` suffix to indicate UTC time.
#### `format`
* **Type**: `string`
* **Default**: `undefined`
A custom format for displaying and storing the date and/or time using [Day.js format tokens](https://day.js.org/docs/en/display/format). If not specified, the field will use ISO 8601 format.
::: tip Format Recommendation
For data portability, we recommend saving date/time values in ISO 8601 format by omitting the `format` option. Formatting is better handled in your application code. Using custom formats is generally discouraged unless you have a specific need for it, e.g., integrating with a framework that doesn’t support date formatting or requires a specific format.
:::
#### `date_format`
* **Type**: `string` or `boolean`
* **Default**: `true`
A custom format for displaying the date portion using [Day.js format tokens](https://day.js.org/docs/en/display/format). If set to `false`, the date picker will be hidden, making the field time-only. If not specified, the date picker will be shown with the default format.
#### `time_format`
* **Type**: `string` or `boolean`
* **Default**: `true`
A custom format for displaying the time portion using [Day.js format tokens](https://day.js.org/docs/en/display/format). If set to `false`, the time picker will be hidden, making the field date-only. If not specified, the time picker will be shown with the default format.
## Examples
### Date and Time
By default, the DateTime field includes both date/time pickers. The output is in ISO 8601 format:
::: code-group
```yaml [YAML]
- name: eventDateTime
label: Event Date and Time
widget: datetime
```
```toml [TOML]
[[fields]]
name = "eventDateTime"
label = "Event Date and Time"
widget = "datetime"
```
```json [JSON]
{
"name": "eventDateTime",
"label": "Event Date and Time",
"widget": "datetime"
}
```
```js [JavaScript]
{
name: "eventDateTime",
label: "Event Date and Time",
widget: "datetime",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
eventDateTime: 2025-08-15T14:30:00
```
```toml [TOML]
eventDateTime = 2025-08-15T14:30:00
```
```json [JSON]
{
"eventDateTime": "2025-08-15T14:30:00"
}
```
:::
### Date-only
Set `time_format` to `false` to hide the time picker and make the input [date only](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date):
::: code-group
```yaml [YAML]
- name: startDate
label: Start Date
widget: datetime
time_format: false
```
```toml [TOML]
[[fields]]
name = "startDate"
label = "Start Date"
widget = "datetime"
time_format = false
```
```json [JSON]
{
"name": "startDate",
"label": "Start Date",
"widget": "datetime",
"time_format": false
}
```
```js [JavaScript]
{
name: "startDate",
label: "Start Date",
widget: "datetime",
time_format: false,
}
```
:::
Output example:
::: code-group
```yaml [YAML]
startDate: 2025-08-15
```
```toml [TOML]
startDate = 2025-08-15
```
```json [JSON]
{
"startDate": "2025-08-15"
}
```
:::
### Time-only
Set `date_format` to `false` to hide the date picker and make the input [time only](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/time):
::: code-group
```yaml [YAML]
- name: startTime
label: Start Time
widget: datetime
date_format: false
```
```toml [TOML]
[[fields]]
name = "startTime"
label = "Start Time"
widget = "datetime"
date_format = false
```
```json [JSON]
{
"name": "startTime",
"label": "Start Time",
"widget": "datetime",
"date_format": false
}
```
```js [JavaScript]
{
name: "startTime",
label: "Start Time",
widget: "datetime",
date_format: false,
}
```
:::
Output example:
::: code-group
```yaml [YAML]
startTime: 14:30:00
```
```toml [TOML]
startTime = 14:30:00
```
```json [JSON]
{
"startTime": "14:30:00"
}
```
:::
### UTC Picker and Default Now
Set `picker_utc` to `true` to use UTC time in the date/time picker. The `default` option is set to `{{now}}` to use the current date and time as the default value:
::: code-group
```yaml [YAML]
- name: eventDateTimeUtc
label: Event Date and Time (UTC)
widget: datetime
picker_utc: true
default: '{{now}}'
```
```toml [TOML]
[[fields]]
name = "eventDateTimeUtc"
label = "Event Date and Time (UTC)"
widget = "datetime"
picker_utc = true
default = "{{now}}"
```
```json [JSON]
{
"name": "eventDateTimeUtc",
"label": "Event Date and Time (UTC)",
"widget": "datetime",
"picker_utc": true,
"default": "{{now}}"
}
```
```js [JavaScript]
{
name: "eventDateTimeUtc",
label: "Event Date and Time (UTC)",
widget: "datetime",
picker_utc: true,
default: '{{now}}',
}
```
:::
Output example:
::: code-group
```yaml [YAML]
eventDateTimeUtc: 2025-08-15T14:30:00.000Z
```
```toml [TOML]
eventDateTimeUtc = 2025-08-15T14:30:00.000Z
```
```json [JSON]
{
"eventDateTimeUtc": "2025-08-15T14:30:00.000Z"
}
```
:::
### Custom Format
The `format` option allows specifying a custom format for both displaying and storing the date and/or time using [Day.js format tokens](https://day.js.org/docs/en/display/format). For example, to use the format `MM/DD/YYYY HH:mm`:
::: code-group
```yaml [YAML]
- name: eventDateTime
label: Event Date and Time
widget: datetime
format: MM/DD/YYYY HH:mm
```
```toml [TOML]
[[fields]]
name = "eventDateTime"
label = "Event Date and Time"
widget = "datetime"
format = "MM/DD/YYYY HH:mm"
```
```json [JSON]
{
"name": "eventDateTime",
"label": "Event Date and Time",
"widget": "datetime",
"format": "MM/DD/YYYY HH:mm"
}
```
```js [JavaScript]
{
name: "eventDateTime",
label: "Event Date and Time",
widget: "datetime",
format: "MM/DD/YYYY HH:mm",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
eventDateTime: 08/15/2025 14:30
```
```toml [TOML]
eventDateTime = "08/15/2025 14:30"
```
```json [JSON]
{
"eventDateTime": "08/15/2025 14:30"
}
```
:::
---
---
url: /en/docs/deployments.md
description: >-
Deploy Sveltia CMS with CI/CD automation and control automatic deployment
settings.
---
# Deployments
Sveltia CMS is a headless CMS, so it doesn’t involve any specific deployment process. You can deploy your project like any other web application by using your preferred hosting service or platform.
## CI/CD Integration
In most cases, you may want to automatically deploy your website whenever content is saved in your Git repository via Sveltia CMS. This can be achieved by integrating with a [CI/CD](https://en.wikipedia.org/wiki/CI/CD) (Continuous Integration/Continuous Deployment) service that monitors your Git repository for changes and triggers a build and deployment process.
All the supported Git backends offer their own CI/CD solutions or can be easily integrated with popular third-party CI/CD services. Here are some examples:
* Building: [GitHub Actions](https://github.com/features/actions), [GitLab CI/CD](https://docs.gitlab.com/ci/), [Gitea Actions](https://docs.gitea.com/usage/actions/overview), [Forgejo Actions](https://forgejo.org/docs/latest/user/actions/reference/), [Cloudflare Pages](https://pages.cloudflare.com/), [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com/), [CircleCI](https://circleci.com/), [Travis CI](https://www.travis-ci.com/), [Jenkins](https://www.jenkins.io/), etc.
* Hosting: [GitHub Pages](https://docs.github.com/en/pages), [GitLab Pages](https://docs.gitlab.com/user/project/pages/), [Cloudflare Pages](https://pages.cloudflare.com/), [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com/), [Amazon EC2](https://aws.amazon.com/ec2/), [Firebase Hosting](https://firebase.google.com/products/hosting), [DigitalOcean Droplets](https://www.digitalocean.com/products/droplets), etc.
Your choice of CI/CD service may depend on factors such as ease of use, pricing, performance, and integration with your existing workflow. Some services only host completely static sites, while others can handle dynamic applications as well. Refer to the documentation of your chosen CI/CD and hosting providers for specific instructions on how to set up the deployment process.
## Disabling Automatic Deployments
You may already have a CI/CD tool set up on your Git repository to automatically deploy changes to production. Occasionally, you make a lot of changes to your content to quickly reach the CI/CD provider’s (free) build limits, or you just don’t want to see builds triggered for every single small change.
With Sveltia CMS, you can disable automatic deployments by default and manually trigger deployments at your convenience. This is done by adding the `[skip ci]` prefix to commit messages, the convention supported by [GitHub Actions](https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs), [GitLab CI/CD](https://docs.gitlab.com/ee/ci/pipelines/#skip-a-pipeline), [CircleCI](https://circleci.com/docs/skip-build/#skip-jobs), [Travis CI](https://docs.travis-ci.com/user/customizing-the-build/#skipping-a-build), [Netlify](https://docs.netlify.com/site-deploys/manage-deploys/#skip-a-deploy), [Cloudflare Pages](https://developers.cloudflare.com/pages/platform/branch-build-controls/#skip-builds) and others.
### Configuration
Here are the steps to use this feature:
1. Add the `skip_ci` property to your `backend` configuration with a value of `true`:
::: code-group
```yaml{5} [YAML]
backend:
name: github
repo: owner/repo
branch: main
skip_ci: true
```
```toml{5} [TOML]
[backend]
name = "github"
repo = "owner/repo"
branch = "main"
skip_ci = true
```
```json{6} [JSON]
{
"backend": {
"name": "github",
"repo": "owner/repo",
"branch": "main",
"skip_ci": true
}
}
```
```js{6} [JavaScript]
{
backend: {
name: "github",
repo: "owner/repo",
branch: "main",
skip_ci: true,
},
}
```
:::
2. Commit and deploy the change to the config file and reload the CMS.
3. Now, whenever you save an entry or asset, `[skip ci]` is automatically added to each commit message. However, deletions are always committed without the prefix to avoid unexpected data retention on your site.
4. If you want to deploy a new or updated entry, as well as any other unpublished entries and assets, click an arrow next to the Save button in the Content Editor, then select **Save and Publish**. This will trigger CI/CD by omitting `[skip ci]`.
If you set `skip_ci` to `false`, the behavior is reversed. CI/CD will be triggered by default, while you have an option to **Save without Publishing** that adds `[skip ci]` only to the associated commit.
::: warning Deprecation Notice
The `automatic_deployments` option has been deprecated in favor of the more intuitive `skip_ci` option and will be removed in Sveltia CMS v1.0.0. If you are upgrading from an older version, update your configuration accordingly: `automatic_deployments: false` is equivalent to `skip_ci: true`, while `automatic_deployments: true` is equivalent to `skip_ci: false`.
:::
::: tip Unpublished vs. Drafts
Unpublished entries and assets are not drafts. Once committed to your repository, those changes can be deployed any time another commit is pushed without `[skip ci]`, or when a manual deployment is triggered.
:::
### Manual Deployment Trigger
If the `skip_ci` property is defined, you can manually trigger a deployment by clicking the **Publish Changes** button on the application header. To use this feature:
#### GitHub Actions
Without any configuration, Publish Changes will [trigger a `repository_dispatch` event](https://docs.github.com/en/rest/repos/repos#create-a-repository-dispatch-event) with the `sveltia-cms-publish` event type. Update your build workflow to receive this event:
```yaml
on:
push:
branches: [$default-branch]
repository_dispatch:
types: [sveltia-cms-publish]
```
#### Other CI/CD Providers
To use Publish Changes with a CI/CD provider other than GitHub Actions, you need to set up a [webhook](https://en.wikipedia.org/wiki/Webhook) in your CI/CD provider that triggers a build when called. Check your provider’s documentation for instructions on how to create a deploy hook URL. Here are some examples:
* [Cloudflare Pages](https://developers.cloudflare.com/pages/configuration/deploy-hooks/)
* [Netlify](https://docs.netlify.com/build/configure-builds/build-hooks/)
* [Vercel](https://vercel.com/docs/deploy-hooks)
Then, configure Sveltia CMS to use this URL:
1. Select Settings under the Account button in the top right corner of the CMS.
2. Select the Advanced tab.
3. Enter the deploy hook URL for your provider.
4. [Configure the CSP](/en/docs/security#ci-cd-providers) if necessary.
::: info Why Deploy Hook URL Is Stored in User Settings
Deploy hook URLs are confidential and cannot be stored in the CMS configuration file, which is typically accessible via a public website. As Sveltia CMS works entirely in the frontend, there is no secure place to store such credentials. This is why they are managed in the user settings, which are stored securely in the browser’s local storage.
In the future, we may provide a way to manage credentials for all users via an edge function called [Sveltia CMS Additions](https://sveltiacms.app/en/docs/roadmap#tbd).
:::
---
---
url: /en/docs/frameworks/docusaurus.md
description: >-
Learn how to integrate Sveltia CMS with Docusaurus, including real-world
examples.
---
# Docusaurus Integration Guide
This guide provides resources and information for integrating Sveltia CMS with [Docusaurus](https://docusaurus.io/), a popular static site generator focused on documentation websites.
## Examples
See real-world examples of Docusaurus integrations in our [Showcase](/en/showcase?framework=docusaurus). Most of the listed sites include links to their source code, so you can explore how they implemented Sveltia CMS with Docusaurus.
## Support for Docusaurus
We have implemented specific features to enhance the integration of Sveltia CMS with Docusaurus:
* If an entry collection has only a Markdown `body` field, the [slug](/en/docs/collections/entries#managing-entry-slugs) and [summary](/en/docs/collections/entries#summaries) of the entries will be generated from a header in the Markdown content, if exists. ([Discussion](https://github.com/sveltia/sveltia-cms/issues/230))
## Development Guide
We’ll be adding a detailed development guide for integrating Sveltia CMS with Docusaurus in the near future. In the meantime, you can refer to the [Decap CMS documentation](https://decapcms.org/docs/docusaurus/), as the basic concepts are similar.
---
---
url: /en/docs/workflows/editorial.md
description: >-
Implement an editorial workflow in Sveltia CMS for content review and
approval.
---
# Editorial Workflow
This is an advanced remote workflow designed for teams that require a review process before changes are merged into the default branch. Editors can submit changes for review, and designated reviewers can approve or request modifications.
::: warning Unimplemented
This feature is not yet supported in Sveltia CMS. It will be added in the near future.
:::
## Use Cases
* Teams of content creators and editors working on collaborative projects.
* Projects that require a formal review and approval process for content changes.
* Situations where content quality and consistency are critical, necessitating oversight.
* Workflows that involve multiple stages of review, such as draft, review, and publish.
## Requirements
The [GitHub](/en/docs/backends/github) or [GitLab](/en/docs/backends/gitlab) backend must be used.
::: info Future Plans
Support for the [Gitea/Forgejo](/en/docs/backends/gitea-forgejo) backend may be added in the future.
:::
## Configuration
Add the `publish_mode` option to the top level of your CMS configuration file:
::: code-group
```yaml [YAML]
publish_mode: editorial_workflow
```
```toml [TOML]
publish_mode = "editorial_workflow"
```
```json [JSON]
{
"publish_mode": "editorial_workflow"
}
```
```js [JavaScript]
{
publish_mode: 'editorial_workflow',
}
```
:::
Additionally, you can enable squash merging for pull/merge requests by adding the `squash_merges` option to the `backend` section of your configuration file. If this options is enabled, all commits in a pull/merge request will be squashed into a single commit when merged. Otherwise, a merge commit will be created.
::: code-group
```yaml{4} [YAML]
backend:
name: github
repo: user/repo
squash_merges: true
```
```toml{4} [TOML]
[backend]
name = "github"
repo = "user/repo"
squash_merges = true
```
```json{5} [JSON]
{
"backend": {
"name": "github",
"repo": "user/repo",
"squash_merges": true
}
}
```
```js{5} [JavaScript]
{
backend: {
name: 'github',
repo: 'user/repo',
squash_merges: true,
},
}
```
:::
See the [GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits) or [GitLab](https://docs.gitlab.com/user/project/merge_requests/squash_and_merge/) documentation for more information about squash merging.
---
---
url: /en/docs/frameworks/eleventy.md
description: >-
Learn how to integrate Sveltia CMS with Eleventy, including starter templates
and real-world examples.
---
# Eleventy Integration Guide
This guide provides resources and information for integrating Sveltia CMS with [Eleventy](https://www.11ty.dev/) (11ty), a simple and flexible static site generator.
## Starter Templates
Here are some starter templates built by the community using Eleventy:
* [Eleventy starter template](https://github.com/danurbanowicz/eleventy-sveltia-cms-starter) by [danurbanowicz](https://github.com/danurbanowicz)
* [ZeroPoint](https://getzeropoint.com/) by [MWDelaney](https://github.com/MWDelaney)
* [Sveleven](https://sveleven.com/) by [anydigital](https://github.com/anydigital)
* [Huwindty](https://github.com/aloxe/huwindty) by [aloxe](https://github.com/aloxe)
::: info Disclaimer
These third-party resources are not necessarily reviewed by the Sveltia CMS team. We are not responsible for their maintenance or support. Please contact the respective authors for any issues or questions.
:::
## Examples
See real-world examples of Eleventy integrations in our [Showcase](/en/showcase?framework=eleventy). Most of the listed sites include links to their source code, so you can explore how they implemented Sveltia CMS with Eleventy.
## Development Guide
We’ll be adding a detailed development guide for integrating Sveltia CMS with Eleventy in the near future. In the meantime, feel free to explore the starter templates and showcase examples for guidance.
---
---
url: /en/docs/collections/entries.md
description: >-
Configure entry collections in Sveltia CMS for managing blog posts, products,
and other content types.
---
# Entry Collections
An entry collection contains multiple entries of the same type. Editors can usually create, edit, and delete entries within the collection. Typical use cases for entry collections include blog posts, tags, products or events. Each entry in the collection is represented by a separate file.
::: tip Note for Netlify/Decap CMS users
In Sveltia CMS, what was previously referred to as a **folder collection** in Netlify/Decap CMS is now called an **entry collection**. This change was made to better reflect the purpose and functionality of these collections within the CMS.
:::
## Creating an Entry Collection
Here is an example configuration defining a simple blog posts collection:
::: code-group
```yaml [YAML]
collections:
- name: blog
label: Blog Posts
label_singular: Blog Post
folder: content/blog
fields:
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]
[[collections]]
name = "blog"
label = "Blog Posts"
label_singular = "Blog Post"
folder = "content/blog"
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]
{
"collections": [
{
"name": "blog",
"label": "Blog Posts",
"label_singular": "Blog Post",
"folder": "content/blog",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
```
```js [JavaScript]
{
collections: [
{
name: "blog",
label: "Blog Posts",
label_singular: "Blog Post",
folder: "content/blog",
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
}
```
:::
### Options
The following options are commonly used when defining an entry collection:
* `name`: (required) A unique identifier for the collection. Should not contain spaces or special characters.
* `label`: A human-readable name for the collection. If omitted, the `name` value is used.
* `label_singular`: A human-readable singular name for the collection. If omitted, the `label` value is used. Used in some parts of the UI like the “Create new” button.
* `description`: A brief description of the collection, displayed in the UI. Basic Markdown formatting is supported, including bold, italic, strikethrough, code, and links.
* `folder`: (required) The folder path where the entries are stored, relative to the repository’s root directory. It can be an empty string (or `.` or `/`) to store entries in the root folder.
* `fields`: (required) An array defining the [fields](/en/docs/fields) for each entry in the collection. Each field has a `name`, `label`, and optional `widget` type.
## File Format and Extension
Sveltia CMS supports various file formats for entry collections, including Markdown, YAML, JSON, and TOML. The default format is Markdown with YAML front matter.
The example below defines a simple blog posts collection:
::: code-group
```yaml [YAML]
collections:
- name: posts
label: Blog Posts
folder: /content/posts
fields:
- { name: title, label: Title }
- { name: date, label: Date, widget: datetime }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "date"
label = "Date"
widget = "datetime"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "date", "label": "Date", "widget": "datetime" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
```
```js [JavaScript]
{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
fields: [
{ name: "title", label: "Title" },
{ name: "date", label: "Date", widget: "datetime" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
}
```
:::
By default, entry collections use the `title` field as the slug (filename). The default format is `yaml-frontmatter` with the `md` extension, meaning each entry will be saved as a Markdown file with YAML front matter. A Markdown field named `body` is treated as the main content of the file, while other fields are stored in the front matter.
If you create a blog post with the title “My First Post”, the file will be saved at `content/posts/my-first-post.md`, with the following content:
::: code-group
```md [my-first-post.md]
---
title: My First Post
date: 2024-06-01T12:00:00Z
---
This is the body of my first post.
```
:::
You can customize the file format using the `format` property of the collection. The example below shows how to use JSON for file format:
::: code-group
```yaml [YAML]{5}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
format: json
```
```toml [TOML]{5}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
format = "json"
```
```json [JSON]{7}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"format": "json"
}
]
}
```
```js [JavaScript]{7}
{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
format: "json",
},
],
}
```
:::
The output file for a post created with this configuration would look like this:
::: code-group
```json [my-first-post.json]
{
"title": "My First Post",
"date": "2024-06-01T12:00:00Z",
"body": "This is the body of my first post."
}
```
:::
### Format
The following file formats are supported for entry collections. You can specify the desired format using the `format` option to define how entries are parsed and saved. The default format is `yaml-frontmatter`.
* `yml` or `yaml`: YAML files with the `yml` extension by default.
* `toml`: TOML files with the `toml` extension by default.
* `json`: JSON files with the `json` extension by default.
* `yaml-frontmatter`: Markdown files with YAML front matter, the `md` extension and the `---` delimiter by default.
* `toml-frontmatter`: Markdown files with TOML front matter, the `md` extension and the `+++` delimiter by default.
* `json-frontmatter`: Markdown files with JSON front matter, the `md` extension and the `{` / `}` delimiter by default.
* `frontmatter`: Markdown files with front matter in any of the supported formats. The format is automatically detected based on the front matter delimiters. However, when creating new entries, the format defaults to `yaml-frontmatter`. The `md` extension and `---` delimiter are used by default.
* `raw`: Raw text files with the `txt` extension by default. When using this format, make sure to have only one field named `body` with the `widget` type set to `code`, `markdown`, `richtext` or `text`. This is useful for a file collection that manages plain text files without any front matter, such as JSON, XML, or CSV files.
The JSON and YAML formats can be customized via the [global `output` option](/en/docs/data-output#controlling-data-output).
::: warning Deprecation Notice
The collection-level `yaml_quote` option has been deprecated in favor of the `quote` option in the [global `output` option](/en/docs/data-output#controlling-data-output). The `yaml_quote` option will be removed in Sveltia CMS v1.0.0. If you are upgrading from an older version, update your configuration accordingly. `yaml_quote: true` is equivalent to `quote: double` in the global YAML format options.
:::
If you want to use a different file format, register a custom format using the [Custom File Formats API](/en/docs/api/file-formats) and specify its name in the `format` option.
### Extension
You can customize the file extension using the `extension` property of the collection. The default extensions for each format are explained above, but you can change them as needed. For example, to use the `markdown` extension for Markdown files with YAML front matter:
::: code-group
```yaml [YAML]{5}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
extension: markdown
```
```toml [TOML]{5}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
extension = "markdown"
```
```json [JSON]{7}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"extension": "markdown"
}
]
}
```
```js [JavaScript]{7}
{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
extension: "markdown",
},
],
}
```
:::
You can use any valid file extension, such as `html`, `txt`, or `mdx`. Just make sure that the file format and extension are compatible. If there is an obvious mismatch, Sveltia CMS will raise a validation error. For example, if you use `json` format with `md` extension, it will result in an error because JSON files should have a `json` extension.
### Front Matter Delimiter
The front matter delimiter can be customized using the `frontmatter_delimiter` option. It accepts either a string or an array of two strings representing the opening and closing delimiters. For example, to use `~~~` as the delimiter for TOML front matter:
::: code-group
```yaml [YAML]{5-6}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
format: toml-frontmatter
frontmatter_delimiter: ~~~ # or [~~~, ~~~]
```
```toml [TOML]{5-6}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
format = "toml-frontmatter"
frontmatter_delimiter = "~~~"
```
```json [JSON]{7-8}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"format": "toml-frontmatter",
"frontmatter_delimiter": "~~~"
}
]
}
```
```js [JavaScript]{7-8}
{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
format: "toml-frontmatter",
frontmatter_delimiter: "~~~",
},
],
}
```
:::
## Managing 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:
::: code-group
```yaml [YAML]
slug:
encoding: unicode
clean_accents: false
sanitize_replacement: '-'
```
```toml [TOML]
[slug]
encoding = "unicode"
clean_accents = false
sanitize_replacement = "-"
```
```json [JSON]
{
"slug": {
"encoding": "unicode",
"clean_accents": false,
"sanitize_replacement": "-"
}
}
```
```js [JavaScript]
{
slug: {
encoding: "unicode",
clean_accents: false,
sanitize_replacement: "-"
},
}
```
:::
The available options are:
* `encoding`: Specifies the encoding method for slugs. Supported values are `unicode` (default) and `ascii`.
* `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 the `sanitize_replacement` option.
* `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., `é` becomes `e`). Also, certain characters like German umlauts are [transliterated](https://en.wikipedia.org/wiki/Transliteration) to their ASCII equivalents (e.g., `ß` becomes `ss`). The default value is `false`.
* `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 is `undefined`, meaning there is no length limit.
* `trim`: A boolean value indicating whether to trim leading and trailing `sanitize_replacement` characters from the slug. The default value is `true`.
* `lowercase`: A boolean value indicating whether to convert the slug to lowercase. The default value is `true`. Changing this to `false` will preserve the original casing of the title or identifier field.
::: warning 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](/en/docs/frameworks/vitepress) or [Docusaurus](/en/docs/frameworks/docusaurus) setup. If no title or header is found, a part of a random UUID will be used to ensure uniqueness.
### Defining Entry Slugs
The `slug` option allows you to define a custom template for generating entry slugs using various [template tags](#slug-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:
::: code-group
```yaml [YAML]{5}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
slug: '{{year}}-{{month}}-{{slug}}'
```
```toml [TOML]{5}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
slug = "{{year}}-{{month}}-{{slug}}"
```
```json [JSON]{7}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"slug": "{{year}}-{{month}}-{{slug}}"
}
]
}
```
```js [JavaScript]{7}
{
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}}`.
To use a field named `slug`, you need to prefix it with `fields.`, like `{{fields.slug}}`, to avoid confusion with the `slug` template tag itself.
You can use [string transformations](/en/docs/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:
::: code-group
```yaml [YAML]{5}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
slug: "{{date | date('YYYY-MM-DD')}}-{{fields.slug}}"
```
```toml [TOML]{5}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
slug = "{{date | date('YYYY-MM-DD')}}-{{fields.slug}}"
```
```json [JSON]{7}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"slug": "{{date | date('YYYY-MM-DD')}}-{{fields.slug}}"
}
]
}
```
```js [JavaScript]{7}
{
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](#using-subfolders) instead.
:::
### Slug Template Tags
The following template tags are supported in the `slug` option:
* `{{slug}}`: The slugified version of the entry’s `title` field (or the field defined with the `identifier_field` option).
* `{{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.
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, entry slugs are automatically generated based on the `title` field or the template defined in the `slug` option. However, you can allow users to edit the slug directly in the entry editor by including a special slug field in the `slug` option.
To make the slug editable, set the `slug` option to `{{fields._slug}}`. This will display a special slug editor UI that looks like a standard string field, but the value will be used as the entry slug.
Once the entry is created, the `_slug` field will be populated with the generated slug. Users can then modify it as needed using the slug editor, which can be accessed via the 3-dot menu in the entry editor.
To make the slug editable for each locale in an [i18n](/en/docs/i18n)-enabled collection, use set the `slug` option to `{{fields._slug | localize}}`.
## Managing 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](#defining-entry-slugs) described above, the `path` option can use template tags to create dynamic folder structures. The [slug template tags](#slug-template-tags) and [string transformations](/en/docs/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:
::: code-group
```yaml [YAML]{5}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
path: '{{year}}/{{month}}/{{slug}}'
```
```toml [TOML]{5}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
path = "{{year}}/{{month}}/{{slug}}"
```
```json [JSON]{7}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"path": "{{year}}/{{month}}/{{slug}}"
}
]
}
```
```js [JavaScript]{7}
{
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](https://gohugo.io/content-management/page-bundles/) using the `path`, [`media_folder` and `public_folder` options](/en/docs/media/internal#using-entry-relative-folders) together. For example, to create a leaf bundle for each blog post, you can use the following configuration:
::: code-group
```yaml [YAML]{5-7}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
path: '{{slug}}/index'
media_folder: ''
public_folder: ''
```
```toml [TOML]{5-7}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
path = "{{slug}}/index"
media_folder = ""
public_folder = ""
```
```json [JSON]{7-9}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"path": "{{slug}}/index",
"media_folder": "",
"public_folder": ""
}
]
}
```
```js [JavaScript]{7-9}
{
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.
### 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 [`i18n`](/en/docs/i18n) global or collection option (optional)
* It can be configured to add internationalization (i18n) support to your site.
* The `structure` and `omit_default_locale_from_file_path` options affect the entry file path.
* The `folder` collection 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 [`path`](#using-subfolders) collection option (optional)
* It defaults to `{{slug}}`, which is the `slug` collection option value.
* It can contain template tags.
* It can also contain slashes to create a nested folder structure.
* The [`slug`](#managing-entry-slugs) collection option (optional)
* It defaults to `{{title}}`, which is the entry’s `title` field value’s slugified version.
* It can contain template tags but *cannot* contain slashes.
* The [`extension`](#extension) collection option (optional)
* It defaults to `md`.
Looking at the above options, the entry file path can be constructed as follows:
* With i18n disabled:
```yaml
//.
```
* With the `single_file` i18n structure
```yaml
//.
```
* With the `multiple_files` i18n structure:
```yaml
//..
```
When the `omit_default_locale_from_file_path` i18n option is set to `true`, the path depends on the locale:
```yaml
//. # default locale
//.. # other locales
```
* With the `multiple_folders` i18n structure:
```yaml
///.
```
When the `omit_default_locale_from_file_path` i18n option is set to `true`, the path depends on the locale:
```yaml
//. # default locale
///. # other locales
```
* With the `multiple_root_folders` i18n structure:
```yaml
///.
```
When the `omit_default_locale_from_file_path` i18n option is set to `true`, the path depends on the locale:
```yaml
//. # default locale
///. # other locales
```
### Creating Editable Nested Structures
With the `nested` and `meta` options, you can organize contents that have a hierarchical relationship, such as categories and subcategories, and allow editors to create nested entries easily. This feature is called **nested collections** in Netlify/Decap CMS.
::: warning Unimplemented
This feature is not yet supported in Sveltia CMS. It will be added in the near future.
:::
## Managing Preview Paths
The `preview_path` option allows you to define a custom URL path for previewing entries on your live site. This option accepts a string with template tags that will be replaced with entry-specific values when generating the preview URL. The CMS provides links to preview the entries based on this URL structure.
The [slug template tags](#slug-template-tags) can be used in the `preview_path` option, with the following exceptions:
* `{{slug}}`: the entire slug of the entry, not just the slugified entry identifier.
* `{{year}}`, `{{month}}`, `{{day}}`, `{{hour}}`, `{{minute}}`, `{{second}}`: these tags are based on the entry’s [DateTime field](/en/docs/fields/datetime). The CMS looks for the first DateTime field in the collection to extract the date and time information. Use the `preview_path_date_field` option to specify a different date field. If no DateTime field is found, the `preview_path` option will be ignored.
* `{{dirname}}`: the directory name of the entry file relative to the collection `folder`. This is useful when using the `path` option to create subfolders.
* `{{filename}}`: the filename of the entry without the extension. This is useful when you want to use the exact filename in the preview URL.
* `{{extension}}`: the file extension of the entry. This is useful when you want to include the file type in the preview URL.
* `{{locale}}`: the locale code of the entry when using [i18n support](/en/docs/i18n). This is useful when you want to include the locale in the preview URL.
You can use [string transformations](/en/docs/string-transformations) with these template tags.
The example below shows how to configure a blog posts collection with a custom preview URL structure.
::: code-group
```yaml [YAML]{5-6}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
preview_path: '/blog/{{year}}/{{month}}/{{slug}}'
preview_path_date_field: created_at
fields:
- { name: title, label: Title }
- { name: created_at, label: Created At, widget: datetime }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]{5-6}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
preview_path = "/blog/{{year}}/{{month}}/{{slug}}"
preview_path_date_field = "created_at"
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "created_at"
label = "Created At"
widget = "datetime"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]{7-8}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"preview_path": "/blog/{{year}}/{{month}}/{{slug}}",
"preview_path_date_field": "created_at",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "created_at", "label": "Created At", "widget": "datetime" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
```
```js [JavaScript]{7-8}
{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
preview_path: "/blog/{{year}}/{{month}}/{{slug}}",
preview_path_date_field: "created_at",
fields: [
{ name: "title", label: "Title" },
{ name: "created_at", label: "Created At", widget: "datetime" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
}
```
:::
With the above configuration, a blog post created on June 15, 2025, with the title “My First Post” will have a preview URL of `/blog/2025/06/my-first-post`.
## Controlling Entry Creation
Sveltia CMS provides options to control entry creation and deletion, limit the number of entries, and hide collections from the Sveltia CMS interface.
### Disabling Creation and Deletion
You can disable entry creation and deletion by setting the `create` option to `false` and the `delete` option to `false` in the collection definition. This is useful for collections where entries are managed programmatically or through other means, and you don’t want editors to create or delete entries.
::: code-group
```yaml [YAML]{5-6}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
create: false
delete: false
fields:
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]{5-6}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
create = false
delete = false
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]{7-8}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"create": false,
"delete": false,
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
```
```js [JavaScript]{7-8}
{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
create: false,
delete: false,
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
}
```
:::
::: warning Breaking change from Netlify/Decap CMS
In Sveltia CMS, the `create` option for entry collections defaults to `true` because, in 99.99% of cases, users want to create new entries and adding `create: true` to every collection is redundant. To disable entry creation, set `create: false` explicitly.
:::
### Limiting Entry Count
You can limit the number of entries in an entry collection using the `limit` option. This is useful for collections where you want to restrict the number of items, such as featured articles or top products.
::: code-group
```yaml [YAML]{5}
collections:
- name: featured_articles
label: Featured Articles
folder: /content/featured_articles
limit: 5
fields:
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]{5}
[[collections]]
name = "featured_articles"
label = "Featured Articles"
folder = "/content/featured_articles"
limit = 5
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]{7}
{
"collections": [
{
"name": "featured_articles",
"label": "Featured Articles",
"folder": "/content/featured_articles",
"limit": 5,
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
```
```js [JavaScript]{7}
{
collections: [
{
name: "featured_articles",
label: "Featured Articles",
folder: "/content/featured_articles",
limit: 5,
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
}
```
:::
With the above configuration, editors can only create up to 5 entries in the `featured_articles` collection. Once the limit is reached, the “Create new” button will be disabled in the Sveltia CMS interface.
### Hiding the Collection
You can hide an entry collection from the Sveltia CMS interface using the `hide` option. This is useful for collections that are managed programmatically or through other means, and you don’t want editors to see or modify them.
::: code-group
```yaml [YAML]{5}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
hide: true
fields:
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]{5}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
hide = true
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]{7}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"hide": true,
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
```
```js [JavaScript]{7}
{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
hide: true,
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
}
```
:::
When the `hide` option is set to `true`, the collection will not appear in the Sveltia CMS interface, and editors will not be able to access or modify its entries.
## Including and Excluding Entries
Sometimes, you may want to include or exclude specific entries from being displayed in the Sveltia CMS interface. Sveltia CMS provides options to manage this.
### Managing Hugo’s Special Index file
By default, Hugo’s [special `_index.md` file](https://gohugo.io/content-management/organization/#index-pages-_indexmd) are hidden in a folder collection unless the `path` option is configured to end with `_index` and the `extension` is set to `md`. You have to create a [file collection](/en/docs/collections/files) to manage the file, since it usually comes with a different set of fields than regular entry fields.
The `index_file` option allows you to include and manage the special index file within the same folder collection. This way, editors can easily access and edit the index file alongside regular entries.
::: code-group
```yaml [YAML]{10-13}
collections:
- name: posts
label: Blog posts
folder: /content/posts
fields: # Fields for regular entries
- { name: title, label: Title }
- { name: date, label: Published Date, widget: datetime }
- { name: description, label: Description }
- { name: body, label: Body, widget: richtext }
index_file:
fields: # Fields for the index file
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]{24-33}
[[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 = "description"
label = "Description"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
[collections.index_file]
[[collections.index_file.fields]]
name = "title"
label = "Title"
[[collections.index_file.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]{13-18}
{
"collections": [
{
"name": "posts",
"label": "Blog posts",
"folder": "/content/posts",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "date", "label": "Published Date", "widget": "datetime" },
{ "name": "description", "label": "Description" },
{ "name": "body", "label": "Body", "widget": "richtext" }
],
"index_file": {
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
}
]
}
```
```js [JavaScript]{13-18}
{
collections: [
{
name: "posts",
label: "Blog posts",
folder: "/content/posts",
fields: [
{ name: "title", label: "Title" },
{ name: "date", label: "Published Date", widget: "datetime" },
{ name: "description", label: "Description" },
{ name: "body", label: "Body", widget: "richtext" },
],
index_file: {
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
},
],
}
```
:::
Here is an example of full customization. All options are optional.
::: code-group
```yaml [YAML]
index_file:
name: _index # File name without a locale or extension. Default: _index
label: Index File # Human-readable file label. Default: Index File
icon: home # Material Symbols icon name. Default: home
fields: # Fields for the index file. If omitted, regular entry fields are used
...
editor:
preview: false # Hide the preview pane if needed. Default: true
```
```toml [TOML]
[index_file]
name = "_index"
label = "Index File"
icon = "home"
# fields would be defined as [[index_file.fields]] elements
# editor configuration
[index_file.editor]
preview = false
```
```json [JSON]
{
"index_file": {
"name": "_index",
"label": "Index File",
"icon": "home",
"fields": [],
"editor": {
"preview": false
}
}
}
```
```js [JavaScript]
{
index_file: {
name: "_index",
label: "Index File",
icon: "home",
fields: [],
editor: {
preview: false,
},
},
}
```
:::
If your regular entry fields and index file fields are identical and you don’t need any options, simply write:
::: code-group
```yaml [YAML]
index_file: true
```
```toml [TOML]
index_file = true
```
```json [JSON]
{
"index_file": true
}
```
```js [JavaScript]
{
index_file: true,
}
```
:::
Note that the special index file is placed right under the `folder`, regardless of the collection’s [`path` option](#using-subfolders). For example, if the `path` is `{{year}}/{{slug}}`, a regular entry would be saved as `content/posts/2025/title.md`, but the index file remains at `content/posts/_index.md`.
### Filtering Entries
With the `filter` option, you can limit the entries displayed in the Sveltia CMS interface based on specific criteria. This is useful for collections where you want to show only a subset of entries, such as a specific language or category.
This option takes an object with two properties:
* `field`: The name of the field to filter by.
* `value` or `pattern`: The value that the field must match for an entry to be included.
* The `value` property checks for exact matches, while the `pattern` property allows for regular expression matching.
* The `value` can be a single value or an array of values. If an array is provided, entries matching any of the values will be included. If `null` is provided as a value, entries where the field is not set will be included.
* The `pattern` property should be a string representing a valid regular expression.
The example below shows how to create two separate collections for English and French blog posts, filtering entries based on the `lang` field:
::: code-group
```yaml [YAML]{5,13}
collections:
- name: english-posts
label: English Posts
folder: /content/posts
filter: { field: lang, value: en }
fields:
- { name: lang, label: Language, widget: select, options: [en, fr] }
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
- name: french-posts
label: French Posts
folder: /content/posts
filter: { field: lang, value: fr }
fields:
- { name: lang, label: Language, widget: select, options: [en, fr] }
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]{5,26}
[[collections]]
name = "english-posts"
label = "English Posts"
folder = "/content/posts"
filter = { field = "lang", value = "en" }
[[collections.fields]]
name = "lang"
label = "Language"
widget = "select"
options = ["en", "fr"]
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
[[collections]]
name = "french-posts"
label = "French Posts"
folder = "/content/posts"
filter = { field = "lang", value = "fr" }
[[collections.fields]]
name = "lang"
label = "Language"
widget = "select"
options = ["en", "fr"]
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]{7,18}
{
"collections": [
{
"name": "english-posts",
"label": "English Posts",
"folder": "/content/posts",
"filter": { "field": "lang", "value": "en" },
"fields": [
{ "name": "lang", "label": "Language", "widget": "select", "options": ["en", "fr"] },
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
},
{
"name": "french-posts",
"label": "French Posts",
"folder": "/content/posts",
"filter": { "field": "lang", "value": "fr" },
"fields": [
{ "name": "lang", "label": "Language", "widget": "select", "options": ["en", "fr"] },
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
```
```js [JavaScript]{7,18}
{
collections: [
{
name: "english-posts",
label: "English Posts",
folder: "/content/posts",
filter: { field: "lang", value: "en" },
fields: [
{ name: "lang", label: "Language", widget: "select", options: ["en", "fr"] },
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
{
name: "french-posts",
label: "French Posts",
folder: "/content/posts",
filter: { field: "lang", value: "fr" },
fields: [
{ name: "lang", label: "Language", widget: "select", options: ["en", "fr"] },
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
}
```
:::
## Managing Entry Listings
After creating an entry collection, you might want to customize how entries are displayed in the Sveltia CMS interface. The following options allow you to control various aspects of entry listings.
### Summaries
By default, Sveltia CMS uses the `title` field (or a field defined with the `identifier_field` option) as the summary for each entry in the listing view.
Sometimes entries might only have a `body` field without a `title` field. In such cases, Sveltia CMS will look for a header in the Markdown body field, if it exists, or use the entry slug as a fallback to ensure that the summary is never empty. This behavior supports typical Markdown-based setups like [VitePress](/en/docs/frameworks/vitepress) and [Docusaurus](/en/docs/frameworks/docusaurus).
You can customize the summary displayed for each entry using the `summary` option. This option accepts a string with template tags that will be replaced with entry-specific values when generating the summary. For example, to display both the title and date of each entry in the summary, you can use the following configuration:
::: code-group
```yaml [YAML]
summary: '{{title}} ({{date}})'
```
```toml [TOML]
summary = "{{title}} ({{date}})"
```
```json [JSON]
{
"summary": "{{title}} ({{date}})"
}
```
```js [JavaScript]
{
summary: "{{title}} ({{date}})",
}
```
:::
Basic Markdown syntax is supported in the `summary` option, including bold, italics and inline code. For example:
::: code-group
```yaml [YAML]
summary: '**{{title}}** - _{{date}}_ `{{status}}`'
```
```toml [TOML]
summary = "**{{title}}** - _{{date}}_ `{{status}}`"
```
```json [JSON]
{
"summary": "**{{title}}** - _{{date}}_ `{{status}}`"
}
```
```js [JavaScript]
{
summary: "**{{title}}** - _{{date}}_ `{{status}}`",
}
```
:::
You can use [string transformations](/en/docs/string-transformations) with these template tags as well. For example:
::: code-group
```yaml [YAML]
summary: "{{title}} - {{date | date('DD MMM YYYY')}} {{published | ternary('', '(draft)')}}"
```
```toml [TOML]
summary = "{{title}} - {{date | date('DD MMM YYYY')}} {{published | ternary('', '(draft)')}}"
```
```json [JSON]
{
"summary": "{{title}} - {{date | date('DD MMM YYYY')}} {{published | ternary('', '(draft)')}}"
}
```
```js [JavaScript]
{
summary: "{{title}} - {{date | date('DD MMM YYYY')}} {{published | ternary('', '(draft)')}}",
}
```
:::
The following template tags are supported in the `summary` option, in addition to [slug template tags](#slug-template-tags):
* `{{dirname}}`: The name of the directory containing the entry file, relative to the collection `folder`.
* `{{filename}}`: The entry file name without the extension.
* `{{extension}}`: The entry file extension.
* `{{commit_author}}`: The last commit author of the entry file from Git history (if available).
* `{{commit_date}}`: The last commit date of the entry file from Git history (if available).
* `{{locales}}`: The enabled locales for the entry when using [i18n support](/en/docs/i18n).
::: info Known issue
Git commit information is not available with the GitLab backend due to API limitations.
:::
### Thumbnails
By default, Sveltia CMS automatically looks for any non-nested, non-empty Image or File field in the entry to use as a thumbnail in the entry listing view. However, you can customize this behavior using the `thumbnail` option.
::: code-group
```yaml [YAML]{5}
collections:
- name: posts
label: Blog Posts
folder: /content/posts
thumbnail: featuredImage
fields:
- { name: title, label: Title }
- { name: featuredImage, label: Featured Image, widget: image }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]{5}
[[collections]]
name = "posts"
label = "Blog Posts"
folder = "/content/posts"
thumbnail = "featuredImage"
[[collections.fields]]
name = "title"
label = "Title"
[[collections.fields]]
name = "featuredImage"
label = "Featured Image"
widget = "image"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]{7}
{
"collections": [
{
"name": "posts",
"label": "Blog Posts",
"folder": "/content/posts",
"thumbnail": "featuredImage",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "featuredImage", "label": "Featured Image", "widget": "image" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
```
```js [JavaScript]{7}
{
collections: [
{
name: "posts",
label: "Blog Posts",
folder: "/content/posts",
thumbnail: "featuredImage",
fields: [
{ name: "title", label: "Title" },
{ name: "featuredImage", label: "Featured Image", widget: "image" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
}
```
:::
The `thumbnail` option can take a string or an array of strings representing the field names to be used as thumbnails. The first field that contains a valid image will be used as the thumbnail. For example:
::: code-group
```yaml [YAML]
thumbnail: [thumbnailImage, coverImage]
```
```toml [TOML]
thumbnail = ["thumbnailImage", "coverImage"]
```
```json [JSON]
{
"thumbnail": ["thumbnailImage", "coverImage"]
}
```
```js [JavaScript]
{
thumbnail: ["thumbnailImage", "coverImage"],
}
```
:::
A nested field can be specified using dot notation, e.g. `heroImage.src`. A wildcard in the field name is also supported, e.g. `images.*.src`, to target images in a list field.
Occasionally, you may not have suitable images for thumbnails. For example, your images may have subtle differences or varied aspect ratios. In that case, you can disable the thumbnail feature by setting the `thumbnail` option to `false` or an empty array:
::: code-group
```yaml [YAML]
thumbnail: false
```
```toml [TOML]
thumbnail = false
```
```json [JSON]
{
"thumbnail": false
}
```
```js [JavaScript]
{
thumbnail: false,
}
```
:::
## Managing 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.
### Sorting
By default, Sveltia CMS supports sorting by `title`, `date`, `author` and `description` fields if they exist in the collection. 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.
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 a special `slug` field to sort entries by their slugs.
The example below shows how to enable sorting by custom fields such as `category` and nested fields like `author.name`:
::: code-group
```yaml [YAML]{16}
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 [TOML]{5}
[[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 [JSON]{22}
{
"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 [JavaScript]{17}
{
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"],
},
],
}
```
:::
::: info Extended syntax
Sveltia CMS supports an extended syntax used in [Static CMS](https://staticjscms.netlify.app/docs/collection-overview#sortable-fields) 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 configuration 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`.
:::
::: info Known issue
Git commit information is not available with the GitLab backend due to API limitations.
:::
### 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:
::: code-group
```yaml [YAML]{10-15}
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:
- field: draft
- label: Drafts
field: date
label: Year
pattern: '\d{4}'
```
```toml [TOML]{25-32}
[[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 [JSON]{13-23}
{
"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 [JavaScript]{13-23}
{
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}",
},
],
},
],
}
```
:::
::: info Extended syntax
Sveltia CMS supports an extended syntax used in [Static CMS](https://staticjscms.netlify.app/docs/collection-overview#view-groups) 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
```
To sort the Year group in descending order by date, you can add the `sortable_fields` property as described in the [Sorting](#sorting) section above:
```yaml
sortable_fields:
fields: [date, title]
default:
field: date
direction: descending
```
:::
### 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.
::: code-group
```yaml [YAML]{11-20}
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 [TOML]{29-42}
[[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 [JSON]{14-30}
{
"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 [JavaScript]{14-30}
{
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",
},
],
},
],
}
```
:::
::: info Extended syntax
Sveltia CMS supports an extended syntax used in [Static CMS](https://staticjscms.netlify.app/docs/collection-overview#view-filters) 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
```
:::
---
---
url: /en/docs/api/events.md
description: >-
Use event hooks in Sveltia CMS to execute custom code in response to CMS
events.
---
# Event Hooks
Event hooks allow developers to execute custom code in response to specific events within Sveltia CMS. This feature enables advanced customization and integration with other systems by providing a way to listen for and react to various actions taken within the CMS.
## Overview
To register an event listener, use the `registerEventListener` method on the [`CMS` object](/en/docs/api#accessing-the-cms-object):
```js
CMS.registerEventListener({ name, handler });
```
The `registerEventListener` method allows you to register a callback function (`handler`) that will be invoked when a specific event (`name`) occurs within the CMS. The handler function receives an object containing relevant data about the event, allowing you to perform custom logic based on the event context.
Multiple event listeners can be registered for the same event, and they will be executed in the order they were registered.
### Parameters
* `name` (string): The name of the event to listen for. See the [Supported Events](#supported-events) section for a list of available events.
* `handler` (function): A callback function that will be executed when the event is triggered. See the [Event Handler](#event-handler) section for details on the parameters passed to the handler.
## Supported Events
The following events are supported for event hooks:
* `preSave`: Triggered before an entry is saved. You can modify the entry data before it is persisted.
* `postSave`: Triggered after an entry has been saved.
Additionally, the following events are available when using [Editorial Workflow](/en/docs/workflows/editorial):
* `prePublish`: Triggered before an entry is published. You can modify the entry data before it is persisted.
* `postPublish`: Triggered after an entry has been published.
* `preUnpublish`: Triggered before an entry is unpublished.
* `postUnpublish`: Triggered after an entry has been unpublished.
## Event Handler
The handler function receives an object with the following properties:
* `author`: The author object that contains the `login` (login name) and `name` (display name) of the user who triggered the event. It’s not available for the [Local Workflow](/en/docs/workflows/local) since it doesn’t track user information.
* `entry`: The entry object serialized to an [Immutable Map](https://immutable-js.com/docs/v5/Map/). It contains the following properties:
```js
{
data: { ... }, // Default locale data
i18n: {
[locale]: {
data: { ... } // Non-default locale data
}
},
slug, // Entry slug
path, // Entry path
newRecord, // Boolean indicating if it's a new entry
collection, // Collection name
mediaFiles, // Array of associated media files
}
```
For the `preSave` and `prePublish` events, the handler can return a modified entry object in Immutable Map format to change the data before it is saved. The handler can be asynchronous and return a Promise that resolves to the modified `entry` or entry `data`.
For other events, the return value is ignored.
## Examples
### Modifying Entry Data Before Save
The following example demonstrates how to register a pre-save hook that adds a last modified timestamp to the entry data before it is saved.
```js
CMS.registerEventListener({
name: 'preSave',
handler: ({ entry }) => {
return entry.get('data').set('last_modified', new Date().toISOString());
},
});
```
### Accessing I18n Data
If you have [internationalization](/en/docs/i18n) (i18n) support enabled, localized data can be accessed and modified within the event handlers, under the `i18n` property of the entry object. The following example shows how to read and update localized fields in a pre-save hook, assuming the entry has English (default), French and other locales configured.
```js
CMS.registerEventListener({
name: 'preSave',
handler: ({ entry }) => {
console.info('English Title:', entry.getIn(['data', 'title']));
entry.get('i18n').forEach((localeData, locale) => {
console.info(`Locale (${locale}) Title:`, localeData.getIn(['data', 'title']));
});
return entry.setIn(['i18n', 'fr', 'data', 'title'], 'Titre en Français');
},
});
```
The [`getIn`](https://immutable-js.com/docs/v5/Map/#getIn\(\)) and [`setIn`](https://immutable-js.com/docs/v5/Map/#setIn\(\)) methods from Immutable.js are used to work with nested data structures.
### Accessing Media Files
### Getting Notification of Saved Entries
The following example demonstrates how to register a post-save hook that logs information about the saved entry and the author who made the changes.
```js
CMS.registerEventListener({
name: 'postSave',
handler: ({ author, entry }) => {
console.log(`Entry saved by ${author?.login ?? 'Unknown'}:` entry.toJS());
},
});
```
The [`toJS`](https://immutable-js.com/docs/v5/Map/#toJS\(\)) method from Immutable.js is used to convert the entry object back to a plain JavaScript object for easier logging.
---
---
url: /en/docs/faq.md
description: >-
Frequently asked questions about Sveltia CMS including usage, licensing, and
technical details.
---
# FAQ
This page addresses some of the most frequently asked questions about Sveltia CMS. If you have a question that is not covered here, feel free to ask it on the [Discussions](https://github.com/sveltia/sveltia-cms/discussions) page in our GitHub repository.
## General Questions
### Is Sveltia CMS free to use?
Yes. Sveltia CMS is an open-source project released under the [MIT License](https://choosealicense.com/licenses/mit/). You can use it for free in both personal and commercial projects.
### Why it’s free?
The ongoing development of Sveltia CMS is currently funded by the maintainer personally. Our predecessor, Netlify CMS, was also free and open-source software, and we want to continue that tradition with Sveltia CMS. The product’s target audience is individual developers and small teams who may prefer a simple CMS solution without recurring costs.
Fortunately, the costs of maintaining Sveltia CMS are relatively low. Since it’s not SaaS, we don’t own any cloud infrastructure. Services we use for distribution and hosting, such as GitHub, npm, UNPKG, and Cloudflare Pages, are all available for free. We still need to cover some expenses like domain registration and development tools, but these are manageable for a single developer.
### Can I use Sveltia CMS even though it’s still in beta?
Although it’s still in beta, [many users](/en/showcase) are already using our product for their projects. We can confidently say that Sveltia CMS is [much more stable and reliable than Netlify/Decap CMS](/en/docs/successor-to-netlify-cms), which has been widely used in production for years despite its numerous bugs and inactive development.
However, since it’s still in the beta phase, breaking changes may occur. We recommend keeping an eye on the [release information](/en/docs/releases#release-information) for any updates that may require adjustments to your implementation. We also encourage you to [report any issues](/en/feedback#bug-reports) you encounter in our GitHub repository.
### Are there any plans for a hosted version of Sveltia CMS?
We understand that setting up and maintaining a self-hosted CMS can be challenging for some users. Therefore, we may consider offering a hosted version of Sveltia CMS in the future, depending on user demand and resource availability.
### Who’s using Sveltia CMS?
Sveltia CMS is already being used by hundreds of individuals and organizations for their websites and projects, and the number is growing rapidly. See the [Showcase](/en/showcase) page for some examples.
### Can I use Sveltia CMS for commercial projects?
Of course! Sveltia CMS is released under the MIT License, which allows you to use it for both personal and commercial projects without any restrictions.
### Is Sveltia CMS suitable for large-scale projects?
No. Sveltia CMS is primarily designed for small to medium-sized projects, such as personal blogs, portfolios, and small business websites. For large-scale projects with complex requirements, you might want to consider more robust, commercial CMS solutions.
### Is Sveltia CMS a personal project?
Yes, for now. Sveltia CMS is currently maintained by a single developer, who is also the creator of the project. The [maintainer](/en/docs/intro#about-the-author) has over 20 years of experience with a generalist skill set that covers web development, UX/UI design, localization, documentation, and marketing.
At this point, the codebase still requires significant refactoring and improvements in test coverage, so pull requests from the community are not being accepted yet. However, we welcome feedback, suggestions, and bug reports in our GitHub repository.
We’ll create contributor documentation in the future to encourage community contributions, once the codebase is more stable and maintainable.
### Is Sveltia CMS a hobby project?
No. Sveltia CMS is a serious project developed by an experienced UX engineer, with the goal of providing a reliable and high-quality CMS solution for developers and content creators. It was originally created for the maintainer’s own clients who needed a better alternative to Netlify CMS.
A hobby project wouldn’t [solve hundreds of issues](/en/docs/successor-to-netlify-cms) from a predecessor project, implement numerous new features, maintain a high level of quality and performance, or create comprehensive 80+ page documentation.
### Is Sveltia CMS a student project?
No. The maintainer is a seasoned professional with over 20 years of experience in web development and open source. Because [he looks half his age](https://github.com/kyoshino#fun-facts-about-me), some people mistakenly assume he is a student. But rest assured, Sveltia CMS is a serious project developed by an experienced UX engineer.
### Why did you create Sveltia CMS?
Sveltia CMS was originally created for the maintainer’s freelance clients who wanted to replace their existing Netlify CMS installations, which were becoming increasingly difficult to maintain due to unresolved issues and lack of active development. I18n support was a key requirement for these clients, as they needed to manage content in multiple languages.
### What are the main differences between Sveltia CMS and Netlify/Decap CMS?
Sveltia CMS is a complete rewrite of Netlify CMS with a focus on addressing its shortcomings and providing a better user experience. It’s not a fork of Netlify/Decap CMS, but a new project built from the ground up using [Svelte](https://svelte.dev/) instead of React. Literally everything has been improved, from the architecture and design to the feature set and performance. See the [Successor to Netlify CMS](/en/docs/successor-to-netlify-cms) page for a detailed comparison.
### Why is Sveltia CMS popular even during its early development stage?
Sveltia CMS has quickly gained traction due to its focus on solving the pain points of Netlify/Decap CMS users, its framework-agnostic design, and its commitment to providing a high-quality user experience. The [Jamstack](https://jamstack.org/) community has been eagerly awaiting a modern, actively maintained alternative to Netlify/Decap CMS, and Sveltia CMS has effectively filled that void.
Another contributing factor is the limited number of free, Git-based headless CMS options available. Most of the open source projects in this space are either inactive or tied to specific frameworks, making Sveltia CMS an appealing choice for developers seeking a versatile and reliable solution for small to medium-sized projects.
Additionally, AI tools often recommend Sveltia CMS as a replacement for Netlify/Decap CMS, which has further boosted its visibility and adoption.
### Why is it called Sveltia CMS?
Sveltia is the name of [our parent project](https://github.com/sveltia) that develops tools for the [Svelte](https://svelte.dev/) ecosystem. The name “Sveltia” is derived from the word “Svelte,” which reflects the project’s focus on simplicity, performance, and elegance in web development. It’s also the name of [sea snails](https://en.wikipedia.org/wiki/Sveltia).
Sveltia CMS itself is [framework-agnostic](/en/docs/frameworks) and can be used with any front-end framework or library, including popular Astro and Hugo.
## Development and Maintenance
### Is Sveltia CMS going to be actively maintained?
The maintainer wants to make Sveltia CMS a long-term project that serves the needs of developers and content creators. Therefore, he is committed to providing ongoing maintenance and support for the project for the foreseeable future.
### What is the roadmap for Sveltia CMS?
We have a public [roadmap](/en/docs/roadmap) that outlines our plans for future development. It’s regularly updated based on user feedback and priorities. Check it out to see what’s coming next!
### How often are new releases made?
Usually a few times a week, depending on the number of changes and fixes. We follow a continuous release model, so new features, improvements, and bug fixes are released as soon as they are ready. See the [Releases](/en/docs/releases) page for details.
### Are you going to solve all the remaining Netlify/Decap CMS issues?
No. Some issues are irrelevant to Sveltia CMS due to unsupported features or different design decisions. For example, we don’t support Azure DevOps as a Git service provider, so any issues related to that won’t be addressed.
However, we do plan to address the remaining issues that are relevant and worth solving. We expect the total number of solved issues to exceed 300 by the 1.0 release and 450 in the long term. That’s what we have signed up for as the [true successor to Netlify CMS](/en/docs/successor-to-netlify-cms). A mere spiritual successor wouldn’t bother with that.
### How can I contribute to Sveltia CMS?
See the [Contributing page](https://github.com/sveltia/sveltia-cms/blob/main/CONTRIBUTING.md) for guidelines on how to contribute to Sveltia CMS. We welcome contributions in various forms, including bug reports, feature requests, and documentation improvements. Pull requests are not being accepted at this time, but we encourage you to share your ideas and feedback.
## Features and Functionality
### Is Sveltia CMS framework-agnostic?
Yes. Sveltia CMS is designed to be framework-agnostic. While it’s built with [Svelte](https://svelte.dev/), we only distribute the CMS as a precompiled vanilla JavaScript bundle. You can integrate it with [any front-end framework](/en/docs/frameworks) or library of your choice, or even use it with plain HTML/JavaScript.
However, some [JavaScript API](/en/docs/api) features require you to use React components for customization. This is because the API derives from React-based Netlify/Decap CMS and backward compatibility has to be maintained. We’re planning to support other libraries in the future.
### Does Sveltia CMS support multilingual content?
Yes. Sveltia CMS comes with first-class internationalization (i18n) support, which is even superior to some commercial CMS solutions. You can easily create and manage content in multiple languages. Check out our [i18n documentation](/en/docs/i18n) for more details on how to set it up.
### Can I invite multiple users to collaborate on content?
Yes, but with some limitations.
Sveltia CMS does not have built-in user management features at the moment. You need to ask users to create an account with your chosen Git service provider (e.g., GitHub) and invite them to your repository for collaboration. They don’t need to know anything about the service or Git workflow, as Sveltia CMS handles that for them. In the near future, we plan to introduce built-in user management features to simplify collaboration.
Also, please note that Sveltia CMS currently doesn’t have any mechanism to avoid conflicts when multiple users edit the same content simultaneously. We recommend establishing a workflow among your team members to prevent such conflicts. We plan to solve this issue in future releases.
## Security and Privacy
### Does Sveltia CMS have a privacy policy regarding user data?
No. Sveltia CMS does not collect or store any user data, so we don’t have a privacy policy. All content and user information are stored in your Git repository, which you control. See the [Privacy](/en/docs/privacy) page for more details.
## Technical Questions
### Why did you choose Svelte for building Sveltia CMS?
Svelte offers several advantages that make it an excellent choice for building Sveltia CMS, including its performance, simplicity, and ease of use. Svelte compiles components into highly efficient vanilla JavaScript code, resulting in faster load times and better overall performance compared to traditional frameworks that rely on runtime libraries.
We plan to explore other frameworks like [Ripple](https://www.ripplejs.com/) in the future, but Svelte is our primary choice for now due to its benefits.
### Why don’t you use TypeScript for the codebase?
Because TypeScript is hard to read, especially with the [destructuring syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring) commonly used in modern JavaScript. The maintainer prefers to write code in [TypeScript-flavored JavaScript](https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html), which provides type safety through JSDoc comments without the need for a separate compilation step. It’s cleaner and easier to maintain.
[Svelte](https://devclass.com/2023/05/11/typescript-is-not-worth-it-for-developing-libraries-says-svelte-author-as-team-switches-to-javascript-and-jsdoc/) and [Prism](https://github.com/PrismJS/prism/pull/4000) are two notable projects that have also adopted this approach.
### Why are some of the configuration options written in camelCase and others in snake\_case?
The configuration options in Sveltia CMS are designed to be compatible with the existing Netlify/Decap CMS configuration format, which mostly uses snake\_case, with a few camelCase exceptions. To maintain backward compatibility and make it easier for users to migrate from Netlify/Decap CMS to Sveltia CMS, these naming conventions have been retained. The inconsistency is unfortunate, but it’s a trade-off we made for compatibility.
---
---
url: /en/docs/features.md
description: >-
Explore the powerful features of Sveltia CMS for developers and content
editors in content management.
---
# Features
Sveltia CMS is packed with features to make content management easy and efficient for both developers and content editors. We are committed to continuously improving user experience (UX) and developer experience (DX) across the platform by adding new features and enhancing existing ones.
## Features for Content Editors
UX-focused features designed to streamline content creation and management, making Sveltia CMS a joy to use.
#### Modern and Intuitive UI
Clean and user-friendly [user interface](/en/docs/ui) designed for content editors. Dark mode support, keyboard shortcuts, and drag-and-drop uploads enhance usability.
#### Mobile-Friendly Design
Manage content on the go with full [mobile and tablet support](/en/docs/ui#mobile-support). Responsive design ensures optimal navigation on any device, and QR code login provides quick access.
#### Seamless Digital Asset Management (DAM)
Easily upload, organize, and manage media assets with drag-and-drop support and folder structures. Find assets quickly using powerful search and filtering options.
#### One-Click AI Translation
Quickly [translate content](/en/docs/integrations/translations) into multiple languages using integrated AI translation services. Choose from popular providers like Google Cloud Translation, Anthropic, and OpenAI.
#### Stock Photo Integration
Access millions of [stock photos](/en/docs/integrations/stock-photos) directly within the CMS from providers like Pexels, Pixabay, and Unsplash. Search and insert images without leaving the editor.
#### Instant Full-Text Search
Powerful full-text search across all collections and entries. Quickly find content with instant, relevant results as you type.
#### Accessibility-First Design
Designed with [accessibility](/en/docs/ui#accessibility) standards in mind to ensure all users can navigate and use the CMS effectively. Full keyboard navigation and screen reader support included.
#### Image Optimization
Automatic [image optimization](/en/docs/media/internal#image-optimization) with format conversion (WebP, SVG) and resizing for faster load times and improved performance.
#### Entry Backups
Content entries are [automatically backed](/en/docs/ui/content-editor#auto-saving-drafts) up while editing, allowing editors to restore previous versions seamlessly.
## Features for Developers
DX-focused features to simplify integration, customization, and content modeling, making Sveltia CMS a developer-friendly choice.
#### Framework-Agnostic
Supports any [framework or static site generator](/en/docs/frameworks) (SSG) that can read static files. Easily integrate with Astro, Eleventy, Hugo, Jekyll, SvelteKit, VitePress, and more.
#### Platform-Independent
[Deploy Sveltia CMS](/en/docs/deployments) on any platform, including Vercel, Netlify, Cloudflare Pages, GitHub Pages, and traditional web servers. Disable automatic deployments if needed.
#### Git-Based Content Storage
Leverages Git for [content storage](/en/docs/backends), enabling version control, collaboration, and easy deployment. Compatible with GitHub, GitLab, Gitea, and Forgejo.
#### Local-First
[Work directly with local repositories](/en/docs/workflows/local) without a proxy server, thanks to a modern browser API that enables direct file system access. Great performance and security.
#### PAT Authentication
Quick and secure authentication using a personal access token for Git [backend services](/en/docs/backends). No need for complex OAuth flows, making setup easier for developers.
#### Config Validation and Auto-Completion
JSON Schema-based [configuration validation](/en/docs/config-basics#validation-and-autocomplete) to catch errors early in your config file. IDE support with auto-completion and type hints. TypeScript support for the [API](/en/docs/api).
#### Powerful Configuration Options
Extensive customization options that continue to grow based on community feedback. YAML, TOML, and JSON formats supported for [configuration files](/en/docs/config-basics#supported-formats).
#### Extensible Architecture
Extend Sveltia CMS with the [JavaScript API](/en/docs/api), allowing for dynamic configuration, custom field types, preview templates, editor components, and more.
#### Flexible Content Modeling
Flexible [content modeling](/en/docs/content-modeling) with collections, fields, and widgets. Supports relational fields for linking content across collections, enabling complex data structures.
#### Multiple File Formats
Supports various [content formats](/en/docs/data-output) including Markdown, YAML, JSON, and TOML. The API allows adding custom formats for specialized use cases.
#### First-Class i18n Support
Excellent [internationalization support](/en/docs/i18n) for managing multilingual content. Easy configuration of locales, translation workflows, and language-specific publishing.
#### Great Documentation
Comprehensive documentation to help you get started quickly, with detailed guides, code examples, and best practices. Community forums available for additional support.
#### AI Tools Support
We provide `llms.txt` to help you [use AI tools](/en/docs/config-basics#ai-tools-support) like GitHub Copilot, Claude and ChatGPT more effectively when writing Sveltia CMS configuration files.
## Upcoming Features
Sveltia CMS is continuously evolving. Here are some exciting features planned for future releases. See our [Roadmap](/en/docs/roadmap) for more details.
#### Localized UI
The CMS user interface will be available in [multiple languages](/en/docs/ui#localization), making it easier for non-English speaking users to navigate and manage content.
#### Advanced Workflows
The [Editorial Workflow](/en/docs/workflows/editorial) and [Open Authoring](/en/docs/workflows/open) features are coming in the near future to enhance collaboration and streamline content publishing processes.
#### User Management
Supports users without Git service accounts with granular roles and permissions.
---
---
url: /en/feedback.md
description: >-
Share feedback, report bugs, suggest features, and join discussions about
Sveltia CMS development.
---
# Feedback
We welcome your feedback on Sveltia CMS! Whether you have suggestions for new features, improvements, or general comments, please feel free to share your thoughts.
## Bug Reports
If you encounter a bug in Sveltia CMS, please report it by [creating a new issue](https://github.com/sveltia/sveltia-cms/issues/new?type=bug) on GitHub. Include as much detail as possible, such as:
* Steps to reproduce the issue
* Expected behavior vs. actual behavior
* Screenshots or screen recordings, if applicable
* Your environment details (e.g., Sveltia CMS version, browser, OS)
In simple cases, your bug report may be resolved quickly, often within a day. However, more complex issues may take longer to investigate and fix.
### Reporting Security Issues
Please email us at instead of creating a GitHub issue for security vulnerabilities. We will respond as soon as possible.
Although we do not offer a bug bounty program, if you report a security vulnerability in good faith, we will gladly acknowledge your contribution in our release notes.
## Ideas and Feature Requests
General [feedback](https://github.com/sveltia/sveltia-cms/discussions/new?category=ideas) is also welcome, but please check the [Compatibility](/en/docs/migration/netlify-decap-cms#compatibility) and [Roadmap](/en/docs/roadmap) sections of this site before starting a new discussion — your idea may already be covered.
You can also [request new features](https://github.com/sveltia/sveltia-cms/issues/new?type=feature). However, our primary focus is addressing Netlify/Decap CMS compatibility, which keeps us busy, so response times may vary. However, all feature requests are reviewed and considered as we plan our development roadmap.
## Other Channels
Join us on [Discord](https://discord.com/invite/5hwCGqup5b) for a casual chat. Follow us on [Bluesky](https://bsky.app/profile/sveltiacms.app) for updates and announcements.
---
---
url: /en/docs/fields.md
description: >-
Configure fields in Sveltia CMS with built-in types, validation rules, common
options, and design best practices.
---
# Fields
Each collection requires a `fields` property that defines the structure of the content within the collection. Fields specify the type of data to be collected, such as text, images, dates, or custom types.
## Field Types
A field type determines how a field is rendered and interacted with in the CMS. Each field type has its own set of options, behaviors, validations, and data formats.
::: tip Note for Netlify/Decap CMS users
In Sveltia CMS, what was previously referred to as a **widget** in Netlify/Decap CMS is now called a **field type**. This change was made to better align with common content management terminology, as originally [proposed](https://github.com/decaporg/decap-cms/issues/3719) by Netlify CMS maintainers themselves.
The functionality and configuration options remain the same. The `widget` property is still used in the configuration for backward compatibility.
:::
### Built-in field types
Sveltia CMS includes the following field types out of the box:
* [Boolean](/en/docs/fields/boolean): A toggle switch for true/false values.
* [Code](/en/docs/fields/code): A code editor for various programming languages.
* [Color](/en/docs/fields/color): A color picker.
* [Compute](/en/docs/fields/compute): A read-only field that computes its value based on other fields.
* [DateTime](/en/docs/fields/datetime): A date and time picker.
* [File](/en/docs/fields/file): A file uploader and selector.
* [Hidden](/en/docs/fields/hidden): A hidden field that is not displayed in the UI.
* [Image](/en/docs/fields/image): An variant of [File](/en/docs/fields/file) with image-specific features.
* [KeyValue](/en/docs/fields/keyvalue): A field for storing key-value pairs.
* [List](/en/docs/fields/list): A list of items, which can be of any field type.
* [Map](/en/docs/fields/map): A geo-location picker.
* [Markdown](/en/docs/fields/markdown): An alias of [RichText](/en/docs/fields/richtext).
* [Number](/en/docs/fields/number): A numeric input field.
* [Object](/en/docs/fields/object): A field for storing nested objects.
* [Relation](/en/docs/fields/relation): A field for creating relationships between entries in different collections.
* [RichText](/en/docs/fields/richtext): A rich text editor with Markdown support.
* [Select](/en/docs/fields/select): A dropdown or multi-select field.
* [String](/en/docs/fields/string): A single-line text input.
* [Text](/en/docs/fields/text): A multi-line text input.
* [UUID](/en/docs/fields/uuid): A field that generates a unique identifier.
::: warning Breaking change from Netlify CMS
The deprecated Date widget is not supported in Sveltia CMS (and Decap CMS). Use the DateTime widget instead.
:::
### Custom field types
Developers can create [custom field types](/en/docs/api/field-types) to extend the functionality of Sveltia CMS.
## Designing Fields
When designing fields for a collection, consider the following best practices:
* Use appropriate field types for the data being collected to ensure a good user experience.
* Provide clear labels and hints to guide users in entering data correctly.
* Utilize default values where applicable to streamline data entry.
* Organize fields logically, especially when using nested objects or lists.
* Leverage the [relation field](/en/docs/fields/relation) to create connections between different collections, enhancing data integrity and usability.
* Consider the use of [i18n](/en/docs/i18n) for fields that require localization.
* Take advantage of [field validation](#field-validation) to enforce data integrity.
* Plan for scalability by anticipating future data requirements and structuring fields accordingly.
* Regularly review and update field configurations to adapt to changing content needs.
* Test field configurations thoroughly to ensure they meet user requirements and function as expected.
See also the [Content Modeling Guide](/en/docs/content-modeling) for more in-depth advice on designing content structures.
## Common Options
In addition to field-specific options, all field types support the following common options.
An exception is the Hidden field type that only supports `name`, `widget`, `default` and `i18n` options since it has no UI.
### Required Options
#### `name`
* **Type**: `string`
The unique identifier for the field within a field list. This option is required for all field types, including the [Hidden](/en/docs/fields/hidden) field type. It’s used as the key in the output data and to reference the field in various contexts, such as in [Compute](/en/docs/fields/compute) and [Relation](/en/docs/fields/relation) fields as well as an [entry collection](/en/docs/collections/entries)’s `identifier_field`, `summary`, `sortable_fields`, and so on.
The naming convention for field names is typically `snake_case` or `camelCase` — it’s up to you to choose a consistent style. However, it cannot contain spaces or special characters like a dot (`.`) or an asterisk (`*`).
There are two special field names to be aware of:
* A field named `title` is treated as the default `identifier_field` for an [entry collection](/en/docs/collections/entries), meaning it will be used as the entry title and slug unless another field is explicitly set as the `identifier_field`.
* A field named `body` is treated as the main content of the entry, and its value will be placed below the front matter if the collection uses a front matter format like YAML, TOML, or JSON. See the [Data Output](/en/docs/data-output#understanding-exceptions) documentation for more details.
### Optional Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
A field type. It’s one of the lowercase names of the [built-in field types](#built-in-field-types) or a registered [custom field type](/en/docs/api/field-types) name. If not specified, it defaults to `string`, which is a single-line text input.
#### `label`
* **Type**: `string`
* **Default**: value of the `name` option
The human-readable label for the field. It’s displayed in the UI as the field’s title.
#### `comment`
* **Type**: `string`
* **Default**: `""`
A description or comment for the field. It’s displayed between the field label and the field input in the UI. Basic Markdown formatting is supported, including bold, italics, links, and inline code.
#### `hint`
* **Type**: `string`
* **Default**: `""`
A short description or hint for the field value, which provides additional context to users. It’s displayed below the field input in the UI. Basic Markdown formatting is supported, including bold, italics, links, and inline code.
#### `required`
* **Type**: `boolean` or array of locale codes
* **Default**: `true`
A boolean indicating whether **data input** is required for the field. Unless explicitly set to `false`, fields are required by default, meaning users must provide a value when creating or editing an entry.
If [i18n](/en/docs/i18n) is enabled, the option accepts an array of locale codes to specify which locales require input. For example, `required: [en, fr]` means that input is required for English and French locales only.
If the `omit_empty_optional_fields` [output option](/en/docs/data-output#controlling-data-output) is enabled, this option affects **data output** as well. The default value is `true`, meaning optional fields left empty will be omitted from the output. If set to `false`, optional fields left empty will be included in the output with a value of `null`, empty string, or empty array/object, depending on the field type.
#### `pattern`
* **Type**: `array` of `string` (or `RegExp` in JavaScript API)
An array containing a [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions) pattern and an error message to validate the field’s value. The regular expression can be provided in one of the following formats:
* A string representing the regex pattern without delimiters, e.g., `'^[A-Za-z0-9]+$'`.
* A string representing the regex pattern with delimiters and flags, e.g., `'/^[a-z0-9]+$/i'`. The delimiters must be slashes `/`.
* A `RegExp` object when using the [JavaScript API](/en/docs/api/initialization).
For example, to restrict a string field to only alphanumeric characters, you can use the following configuration:
::: code-group
```yaml [YAML]
pattern:
- '^[A-Za-z0-9]+$'
- 'Only alphanumeric characters are allowed.'
```
```toml [TOML]
pattern = [ "^[A-Za-z0-9]+$", "Only alphanumeric characters are allowed." ]
```
```json [JSON]
"pattern": [
"^[A-Za-z0-9]+$",
"Only alphanumeric characters are allowed."
]
```
```js [JavaScript]
pattern: [/^[A-Za-z0-9]+$/, 'Only alphanumeric characters are allowed.'];
```
:::
#### `readonly`
* **Type**: `boolean`
* **Default**: `false` (except for UUID fields, which default to `true`)
A boolean indicating whether the field is read-only. It’s useful for fields with a default value that should not be modified by users.
#### `preview`
* **Type**: `boolean`
* **Default**: `true`
Whether to show a preview of the field’s value in the entry’s preview pane. This is useful for fields with large content, such as rich text or code fields, where a preview may not be necessary.
#### `i18n`
* **Type**: `boolean` or `duplicate`
* **Default**: `false`
Indicates whether the field supports internationalization (i18n). See the [i18n documentation](/en/docs/i18n#field-level-configuration) for more details.
## Field Validation
All visible fields support various validation options to ensure data integrity. Common validation options include:
* By default, fields are required to be filled out unless the `required` option is explicitly set to `false`. If i18n is enabled for a field, all localized versions of the field are required unless [specified otherwise](/en/docs/i18n#field-level-configuration).
* String-type and some other simple array-type fields support the `pattern` option, which allows you to define a regular expression that the field’s value must match. This is useful for enforcing specific formats.
* Some fields support minimum and maximum values/items/lengths or value types, depending on the field type. For example:
* The [String](/en/docs/fields/string) field supports `minLength` and `maxLength` options as well as the `type` option that can enforce formats like `email` or `url`.
* The [Number](/en/docs/fields/number) field supports `min`, `max` and `value_type` options.
* Other multi-value fields like [List](/en/docs/fields/list) and [KeyValue](/en/docs/fields/keyvalue) support `min` and `max` options.
If more complicated validation logic is needed, consider creating a [custom field type](/en/docs/api/field-types) that implements the desired validation behavior.
## Examples
### Blog Post Fields
Here is an example configuration for fields in a blog post collection:
::: code-group
```yaml [YAML]
fields:
- name: title
label: Title
widget: string
hint: The title of the blog post
default: Untitled Post
- name: published
label: Published
widget: boolean
required: false
default: false
- name: date
label: Publication Date
widget: datetime
default: '{{now}}'
- name: body
label: Body
widget: richtext
```
```toml [TOML]
[[fields]]
name = "title"
label = "Title"
widget = "string"
hint = "The title of the blog post"
default = "Untitled Post"
[[fields]]
name = "published"
label = "Published"
widget = "boolean"
required = false
default = false
[[fields]]
name = "date"
label = "Publication Date"
widget = "datetime"
default = "{{now}}"
[[fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]
{
"fields": [
{
"name": "title",
"label": "Title",
"widget": "string",
"hint": "The title of the blog post",
"default": "Untitled Post"
},
{
"name": "published",
"label": "Published",
"widget": "boolean",
"required": false,
"default": false
},
{
"name": "date",
"label": "Publication Date",
"widget": "datetime",
"default": "{{now}}"
},
{
"name": "body",
"label": "Body",
"widget": "richtext"
}
]
}
```
```js [JavaScript]
fields: [
{
name: 'title',
label: 'Title',
widget: 'string',
hint: 'The title of the blog post',
default: 'Untitled Post',
},
{
name: 'published',
label: 'Published',
widget: 'boolean',
required: false,
default: false,
},
{
name: 'date',
label: 'Publication Date',
widget: 'datetime',
default: '{{now}}',
},
{
name: 'body',
label: 'Body',
widget: 'richtext',
},
];
```
:::
Output data for a blog post using the above configuration might look like this:
::: code-group
```md [Markdown]
---
title: My First Blog Post
published: true
date: 2024-06-15T10:00:00Z
---
# Welcome to my blog
This is the content of my first blog post.
```
```yaml [YAML]
title: My First Blog Post
published: true
date: 2024-06-15T10:00:00Z
body: |
# Welcome to my blog
This is the content of my first blog post.
```
```toml [TOML]
title = "My First Blog Post"
published = true
date = 2024-06-15T10:00:00Z
body = '''# Welcome to my blog
This is the content of my first blog post.
'''
```
```json [JSON]
{
"title": "My First Blog Post",
"published": true,
"date": "2024-06-15T10:00:00Z",
"body": "# Welcome to my blog\n\nThis is the content of my first blog post."
}
```
:::
---
---
url: /en/docs/collections/files.md
description: >-
Configure file collections in Sveltia CMS for managing pre-defined documents
and standalone content.
---
# File Collections
A file collection contains pre-defined files, each representing a single piece of content. Editors can edit the content of these files but cannot create or delete them. Typical use cases for file collections include site settings, homepage content or about pages.
## Creating a File Collection
The example below defines a file collection for managing static pages:
::: code-group
```yaml [YAML]
collections:
- name: pages
label: Pages
files:
- name: about
label: About Page
file: content/pages/about.md
fields:
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]
[[collections]]
name = "pages"
label = "Pages"
[[collections.files]]
name = "about"
label = "About Page"
file = "content/pages/about.md"
[[collections.files.fields]]
name = "title"
label = "Title"
[[collections.files.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]
{
"collections": [
{
"name": "pages",
"label": "Pages",
"files": [
{
"name": "about",
"label": "About Page",
"file": "content/pages/about.md",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
]
}
```
```js [JavaScript]
{
collections: [
{
name: "pages",
label: "Pages",
files: [
{
name: "about",
label: "About Page",
file: "content/pages/about.md",
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
},
],
}
```
:::
Each file in the collection is defined with a `name`, `label`, `file` path, and a set of `fields`. Editors can modify the content of these files through the Sveltia CMS interface.
### Collection Options
A file collection supports the following options:
* `name`: A unique identifier for the collection. Required.
* `label`: A human-readable name for the collection. Optional.
* `icon`: A Material Symbols icon name to represent the collection in the CMS UI. Optional.
* `files`: An array of file definitions within the collection. Required.
### File Options
A file definition within a file collection supports the following options:
* `name`: A unique identifier for the file within the collection. Required.
* `label`: A human-readable name for the file. Optional.
* `icon`: A Material Symbols icon name to represent the file in the CMS UI. Optional.
* `file`: The path to the file in the content repository. Required.
* `format`: The file format (e.g., `yaml`, `json`, `toml`, `yaml-frontmatter`, etc.). Optional. [See below](#file-format-and-extension) for details.
* `fields`: An array of field definitions for the file content. Required.
## File Format and Extension
The file format and extension for each file in a file collection can be customized using the `format` property within each file definition. Sveltia CMS supports various file formats, including Markdown, YAML, JSON, and TOML.
By default, file format is determined based on the file extension. If it is a Markdown file (e.g., `.md`), it uses `yaml-frontmatter` format. For other extensions, it uses the corresponding format (e.g., `.yaml` uses `yaml` format).
To illustrate, here is a file collection with two files using different formats:
::: code-group
```yaml [YAML]{7,13}
collections:
- name: pages
label: Pages
files:
- name: about
label: About Page
file: content/pages/about.json
fields:
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
- name: contact
label: Contact Page
file: content/pages/contact.yaml
fields:
- { name: title, label: Title }
- { name: body, label: Body, widget: richtext }
```
```toml [TOML]{8,22}
[[collections]]
name = "pages"
label = "Pages"
[[collections.files]]
name = "about"
label = "About Page"
file = "content/pages/about.json"
[[collections.files.fields]]
name = "title"
label = "Title"
[[collections.files.fields]]
name = "body"
label = "Body"
widget = "richtext"
[[collections.files]]
name = "contact"
label = "Contact Page"
file = "content/pages/contact.yaml"
[[collections.files.fields]]
name = "title"
label = "Title"
[[collections.files.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]{10,19}
{
"collections": [
{
"name": "pages",
"label": "Pages",
"files": [
{
"name": "about",
"label": "About Page",
"file": "content/pages/about.json",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
},
{
"name": "contact",
"label": "Contact Page",
"file": "content/pages/contact.yaml",
"fields": [
{ "name": "title", "label": "Title" },
{ "name": "body", "label": "Body", "widget": "richtext" }
]
}
]
}
]
}
```
```js [JavaScript]{10,19}
{
collections: [
{
name: "pages",
label: "Pages",
files: [
{
name: "about",
label: "About Page",
file: "content/pages/about.json",
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
{
name: "contact",
label: "Contact Page",
file: "content/pages/contact.yaml",
fields: [
{ name: "title", label: "Title" },
{ name: "body", label: "Body", widget: "richtext" },
],
},
],
},
],
}
```
:::
### Format
To explicitly set the file format, you can add the `format` property to each file definition. This is useful if you want to use TOML or JSON formats for Markdown files. Here is an example:
::: code-group
```yaml [YAML]{4}
collections:
- name: pages
label: Pages
format: json-frontmatter
files:
- name: about
label: About Page
file: content/pages/about.md
```
```toml [TOML]{4}
[[collections]]
name = "pages"
label = "Pages"
format = "json-frontmatter"
[[collections.files]]
name = "about"
label = "About Page"
file = "content/pages/about.md"
```
```json [JSON]{6}
{
"collections": [
{
"name": "pages",
"label": "Pages",
"format": "json-frontmatter",
"files": [
{
"name": "about",
"label": "About Page",
"file": "content/pages/about.md"
}
]
}
]
}
```
```js [JavaScript]{6}
{
collections: [
{
name: "pages",
label: "Pages",
format: "json-frontmatter",
files: [
{
name: "about",
label: "About Page",
file: "content/pages/about.md",
},
],
},
],
}
```
:::
The `format` can be set at the collection level to apply to all files within that collection, or at the individual file level to override the collection setting for specific files.
Note that when specifying a format, ensure that the file extension matches the chosen format to avoid confusion. If there is an obvious mismatch between the file extension and the specified format, Sveltia CMS will raise a validation error.
### Extension
Unlike entry collections, file collections do not support the `extension` option to define allowed file extensions, since each file is pre-defined with a specific path containing its extension.
Extension-less files are supported in file collections. When using extension-less files, it is recommended to explicitly set the `format` property to ensure the correct parsing of the file content. If `format` is not set, it defaults to `yaml-frontmatter`.
See [Editing site deployment configuration files](/en/docs/how-tos#editing-site-deployment-configuration-files) in our how-tos for an example of using extension-less files in a file collection.
### Front Matter Delimiter
As with entry collections, the [`frontmatter_delimiter` option](/en/docs/collections/entries#front-matter-delimiter) can also be used to customize the front matter delimiter for Markdown files, either at the collection or file level. Here is an example of setting both `format` and `frontmatter_delimiter` at the file level:
::: code-group
```yaml [YAML]{8-9}
collections:
- name: pages
label: Pages
files:
- name: about
label: About Page
file: content/pages/about.md
format: toml-frontmatter
frontmatter_delimiter: ~~~
```
```toml [TOML]{9-10}
[[collections]]
name = "pages"
label = "Pages"
[[collections.files]]
name = "about"
label = "About Page"
file = "content/pages/about.md"
format = "toml-frontmatter"
frontmatter_delimiter = "~~~"
```
```json [JSON]{11-12}
{
"collections": [
{
"name": "pages",
"label": "Pages",
"files": [
{
"name": "about",
"label": "About Page",
"file": "content/pages/about.md",
"format": "toml-frontmatter",
"frontmatter_delimiter": "~~~"
}
]
}
]
}
```
```js [JavaScript]{11-12}
{
collections: [
{
name: "pages",
label: "Pages",
files: [
{
name: "about",
label: "About Page",
file: "content/pages/about.md",
format: "toml-frontmatter",
frontmatter_delimiter: "~~~",
},
],
},
],
}
```
:::
## Singletons
The singleton collection is a special type of file collection that allows you to manage a set of pre-defined files without the ability to create or delete them. Singletons are useful for managing site-wide settings or content that should only exist as a single instance. See [Singletons](/en/docs/collections/singletons) for more details.
---
---
url: /en/docs/fields/file.md
description: >-
Upload, manage, and reference files in Sveltia CMS with multiple file type
support.
---
# File Field
The File field type allows users to upload and manage files within the CMS.
::: tip Alternative for images
If you need to limit uploads to images only, consider using the [Image](/en/docs/fields/image) field type instead.
:::
## User Interface
### Editor
A large upload button is displayed for the File field. When it’s is clicked, a file selection dialog with the following features appears:
* Tabs to select files from different sources: field assets, entry assets, file assets, collection assets, and global assets (if the [internal media storage](/en/docs/media/internal) is enabled).
* An option to upload new files by dragging and dropping them into the dialog or by selecting them from the file system.
* An option to enter a URL to select a file from an external source (if `choose_url` option is enabled).
* Integration with [external media storage providers](/en/docs/media#external-storage) if configured.
* Integration with [stock photo providers](/en/docs/integrations/stock-photos) for easy selection of free images (for Image fields only).
* File type filtering based on the `accept` option.
* A search bar to quickly find existing assets.
If the `multiple` option is enabled, users can select multiple files at once. Uploaded files are displayed as a list with options to remove or replace each file.
On desktop, users can drag and drop file(s) directly onto the File field to attach them without opening the file selection dialog.
The CMS prevents the same file from being uploaded twice. It compares the hashes and selects an existing asset instead.
### Preview
A list of uploaded file names with links to access each file. For images, a thumbnail preview is shown.
::: tip CSP Settings
If your site uses a Content Security Policy (CSP), You may need to update it to display external images properly. See the [CSP documentation](/en/docs/security#setting-up-content-security-policy) for more details.
:::
## Data Type
A string representing the URL or path to a file. If `multiple` option is enabled, it will be an array of strings.
If the `required` option is set to `false` and the field is left empty, the value will be an empty string or an empty array, depending on whether `multiple` is enabled.
By default, Sveltia CMS does not slugify uploaded filenames. If your site generator expects hyphenated filenames, you can enable the `slugify_filename` [internal media storage option](/en/docs/media#configuration).
## Data Validation
* If the `required` option is set to `true`, at least one file must be selected.
* If the `multiple` option is enabled, the number of selected files must be between the `min` and `max` limits, if specified.
* The selected file(s) must match the allowed file types specified in the `accept` option, if provided.
* If the `pattern` option is provided, the file URL(s) must match the specified regular expression pattern.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the File field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `file`.
### Optional Options
::: warning Breaking change from Netlify/Decap CMS
Sveltia CMS does not support the `allow_multiple` option. It’s a confusing option that defaults to `true`, and there is a separate option called `media_library.config.multiple`. We have added the new `multiple` option instead, which is more intuitive and works with all media storage providers.
:::
#### `default`
* **Type**: `string` or `array of strings`
* **Default**: `''` or `[]`
The default value for the field. Should be a string for single file upload or an array of strings for multiple file uploads.
#### `multiple`
* **Type**: `boolean`
* **Default**: `false`
Whether to allow uploading or selecting multiple files.
#### `min`
* **Type**: `integer`
* **Default**: `0`
The minimum number of files required. This enables validation to ensure that users upload or select at least this many files. Ignored if `multiple` is set to `false`.
#### `max`
* **Type**: `integer`
* **Default**: `Infinity`
The maximum number of files allowed. This enables validation to prevent users from uploading or selecting more than this many files. Ignored if `multiple` is set to `false`.
#### `choose_url`
* **Type**: `boolean`
* **Default**: `true`
Whether to show the option to choose a file by URL instead of uploading/selecting from the media storage.
#### `accept`
* **Type**: `string`
* **Default**: `undefined`
A comma-separated list of allowed file types (MIME types or file extensions) for upload. For example, to allow only PDF files, set this option to `application/pdf,.pdf`. To allow only image files, set it to `image/*`. If not specified, all file types are allowed. See the [`accept` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept) documentation on MDN for more details.
Image field only accepts AVIF, GIF, JPEG, PNG, WebP or SVG images by default. Other image formats like BMP, HEIC, JPEG XL, PSD, TIFF are excluded. File field has no default restriction.
#### `media_library`
#### `media_libraries`
#### `media_folder`
#### `public_folder`
## Examples
### Basic File Field
This example demonstrates a basic File field that allows users to upload or select a single file.
::: code-group
```yaml [YAML]
- name: document
label: Document
widget: file
```
```toml [TOML]
[[fields]]
name = "document"
label = "Document"
widget = "file"
```
```json [JSON]
{
"fields": [
{
"name": "document",
"label": "Document",
"widget": "file"
}
]
}
```
```js [JavaScript]
{
fields: [
{
name: 'document',
label: 'Document',
widget: 'file',
},
];
}
```
:::
Output example:
::: code-group
```yaml [YAML]
document: /uploads/sample.pdf
```
```toml [TOML]
document = "/uploads/sample.pdf"
```
```json [JSON]
{
"document": "/uploads/sample.pdf"
}
```
:::
### Multiple File Uploads with Restrictions
This example shows a File field configured to allow multiple file uploads with minimum and maximum limits.
::: code-group
```yaml [YAML]
- name: flyers
label: Flyers
widget: file
multiple: true
min: 1
max: 5
accept: application/pdf,.pdf
```
```toml [TOML]
[[fields]]
name = "flyers"
label = "Flyers"
widget = "file"
multiple = true
min = 1
max = 5
accept = "application/pdf,.pdf"
```
```json [JSON]
{
"fields": [
{
"name": "flyers",
"label": "Flyers",
"widget": "file",
"multiple": true,
"min": 1,
"max": 5,
"accept": "application/pdf,.pdf"
}
]
}
```
```js [JavaScript]
{
fields: [
{
name: 'flyers',
label: 'Flyers',
widget: 'file',
multiple: true,
min: 1,
max: 5,
accept: 'application/pdf,.pdf',
},
];
}
```
:::
Output example:
::: code-group
```yaml [YAML]
flyers:
- /uploads/flyer1.pdf
- /uploads/flyer2.pdf
```
```toml [TOML]
flyers = ["/uploads/flyer1.pdf", "/uploads/flyer2.pdf"]
```
```json [JSON]
{
"flyers": ["/uploads/flyer1.pdf", "/uploads/flyer2.pdf"]
}
```
:::
---
---
url: /en/docs/frameworks.md
description: >-
Learn how to integrate Sveltia CMS with popular frameworks like Astro,
Eleventy, Hugo, Jekyll, SvelteKit, and more.
---
# Framework Guides
Sveltia CMS is designed to be framework-agnostic, allowing you to integrate it with a wide range of frameworks and [static site generators](https://jamstack.org/generators/) (SSGs). Whether you’re using Astro, Eleventy, Hugo, Jekyll, SvelteKit, or another framework — or even vanilla JavaScript — Sveltia CMS can fit seamlessly into your development workflow.
## Getting Started
Here are some resources to help you get started with Sveltia CMS in various popular frameworks:
* [Astro](/en/docs/frameworks/astro)
* [Docusaurus](/en/docs/frameworks/docusaurus)
* [Eleventy](/en/docs/frameworks/eleventy)
* [Hugo](/en/docs/frameworks/hugo)
* [Jekyll](/en/docs/frameworks/jekyll)
* [Middleman](/en/docs/frameworks/middleman)
* [Next.js](/en/docs/frameworks/next)
* [Nuxt](/en/docs/frameworks/nuxt)
* [SvelteKit](/en/docs/frameworks/sveltekit)
* [VitePress](/en/docs/frameworks/vitepress)
* [Zola](/en/docs/frameworks/zola)
More framework guides will be added over time.
## Vanilla JavaScript
Using no framework? No problem! Check out our [Vanilla JavaScript Integration Guide](/en/docs/frameworks/none) for tips on how to use Sveltia CMS with plain JavaScript projects.
## Showcase
Explore how Sveltia CMS is being used with various frameworks in our [Showcase](/en/showcase) section. Use the filters to find examples based on your preferred framework. Astro and Hugo are popular choices among our users, but you’ll find a variety of implementations to inspire your own projects.
---
---
url: /en/docs/start.md
description: >-
Get started with Sveltia CMS with installation, configuration, testing, and
deployment instructions.
---
# Getting Started
This guide will help you get Sveltia CMS up and running quickly. Follow the steps below to install, configure, test, and deploy Sveltia CMS.
Migrating from **Netlify CMS**, **Decap CMS** or **Static CMS**? Check out the [Migration Guides](/en/docs/migration) for specific instructions.
::: warning Stable Version Not Yet Available
Sveltia CMS is still in beta. Although it’s already being used by various organizations and individuals in production, there might still be breaking changes before the stable 1.0 release. We recommend keeping an eye on the [release information](/en/docs/releases#release-information) for any updates.
:::
::: warning No Free Setup Support
Sveltia CMS is specifically designed as a [replacement for Netlify/Decap CMS](/en/docs/successor-to-netlify-cms). We are happy to help you migrate, but **we can’t help you set up Sveltia CMS from scratch** through our free support channels. Questions about installation or initial configuration may go unanswered.
:::
## 1. Install
You can use either a starter template or manually install Sveltia CMS into your existing project.
### Starter Templates
While we don’t have official starter templates yet, the community has created several templates for popular frameworks. Here are some you can try:
#### Astro
* [Astros](https://github.com/majesticooss/astros) by [zanhk](https://github.com/zanhk)
* [Astro i18n Starter](https://github.com/yacosta738/astro-cms) by [yacosta738](https://github.com/yacosta738)
* [astro-sveltia-cms](https://github.com/knolljo/astro-sveltia-cms) by [knolljo](https://github.com/knolljo)
#### Eleventy
* [Eleventy starter template](https://github.com/danurbanowicz/eleventy-sveltia-cms-starter) by [danurbanowicz](https://github.com/danurbanowicz)
* [ZeroPoint](https://getzeropoint.com/) by [MWDelaney](https://github.com/MWDelaney)
* [Sveleven](https://sveleven.com/) by [anydigital](https://github.com/anydigital)
* [Huwindty](https://github.com/aloxe/huwindty) by [aloxe](https://github.com/aloxe)
#### Hugo
* [Hugo module](https://github.com/privatemaker/headless-cms) by [privatemaker](https://github.com/privatemaker)
* [Hugolify](https://www.hugolify.io/) by [sebousan](https://github.com/sebousan)
#### Zola
* [Zola Sveltia Source](https://github.com/unicornfantasian/zola-sveltia-source) by [husenunicorn](https://github.com/husenunicorn)
#### Other Frameworks
The Netlify/Decap CMS website has more [templates](https://decapcms.org/docs/start-with-a-template/) and [examples](https://decapcms.org/docs/examples/). You can probably use one of them and [replace the CMS script](/en/docs/migration/netlify-decap-cms#switching-to-sveltia-cms) since they are largely compatible.
::: info Disclaimer
These third-party resources are not necessarily reviewed by the Sveltia CMS team. We are not responsible for their maintenance or support. Please contact the respective authors for any issues or questions.
:::
### Manual Installation
Even without a starter template, you can easily add Sveltia CMS to your existing project. Follow the steps below to set it up.
Sveltia CMS requires a static files folder to serve the admin interface, configuration file, and media assets. First, you need to identify or create your static files folder. This folder is typically named `public` or `static`, depending on your framework or static site generator. If the static folder does not exist, create it in the root of your project.
::: details Common static folder names
Here’s a quick reference for some popular frameworks:
| Framework / SSG | Static Folder Name |
| ------------------------------------------------ | ------------------- |
| Eleventy, GitBook, Jekyll | `/` (root) |
| Pelican | `/content` |
| MkDocs, Docsify | `/docs` |
| Astro, Next.js, Nuxt, Remix, UmiJS, VitePress | `/public` |
| Hexo, Slate | `/source` |
| mdBook | `/src` |
| Docusaurus, Fresh, Gatsby, Hugo, SvelteKit, Zola | `/static` |
| VuePress | `/.vuepress/public` |
If you’re unsure about your framework’s static files folder, please refer to its official documentation.
:::
Create a folder named `admin` (or any name you prefer) inside your site’s static files folder. Then, under the folder, create an `index.html` file and a `config.yml` file with the following content:
::: code-group
```html [index.html]
Sveltia CMS
```
:::
::: code-group
```yaml [config.yml]
# yaml-language-server: $schema=https://unpkg.com/@sveltia/cms/schema/sveltia-cms.json
backend:
name: github
repo: user/repo
media_folder: /static/media
public_folder: /media
collections:
- name: posts
label: Posts
folder: /content/posts
fields:
- { label: Title, name: title, widget: string }
- { label: Body, name: body, widget: richtext }
```
:::
The structure should look like this, if the static files folder is named `static`:
```
.
└─ static/ # Static files folder
└─ admin/ # Admin folder
├─ index.html # CMS interface
└─ config.yml # CMS configuration
```
::: details How It Works
Sveltia CMS is a single-page application (SPA) distributed as a small JavaScript bundle via a content delivery network (CDN). It’s a unique approach that allows you to quickly set up the CMS without installing any dependencies or build tools. See the [Architecture Overview](/en/docs/architecture) for more details.
:::
::: details Common Mistakes
Some AI tools, namely Claude, include a stylesheet `` tag in Sveltia CMS setups, apparently due to confusion with [Static CMS](https://staticjscms.netlify.app/docs/add-to-your-site-cdn), a now-discontinued fork of Netlify CMS. However, Sveltia CMS does not require any additional CSS files, as all the necessary styles are bundled within the JavaScript file. The link is invalid and can be safely omitted.
Similarly, some tools and templates add a `type="module"` attribute to the `
```
Alternatively, you can use the ES module version, which can be imported using the `mjs` file extension. This script is the same as the NPM package version:
```html
```
### Using the NPM Package
Install the `@sveltia/cms` package via your preferred package manager:
::: code-group
```bash [npm]
npm install @sveltia/cms
```
```bash [yarn]
yarn add @sveltia/cms
```
```bash [pnpm]
pnpm add @sveltia/cms
```
```bash [bun]
bun add @sveltia/cms
```
:::
Then, import the `CMS` object in your script to access the initialization and other API methods:
```js
import CMS from '@sveltia/cms';
CMS.init({ config });
CMS.registerPreviewStyle(filePath);
CMS.registerEditorComponent(definition);
```
or import only the methods you need:
```js
import { init } from '@sveltia/cms';
init({ config });
```
TypeScript types are included in the package, so if you edit your project with a TypeScript-aware editor like VS Code, you should get type checking and autocompletion without any additional setup.
## Available Methods
Currently, the following methods are available on the `CMS` object:
* [Manual Initialization](/en/docs/api/initialization): `init`
* [Custom Preview Styles](/en/docs/api/preview-styles): `registerPreviewStyle`
* [Custom Preview Templates](/en/docs/api/preview-templates): `registerPreviewTemplate`
* [Custom Editor Components](/en/docs/api/editor-components): `registerEditorComponent`
* [Custom Field Types](/en/docs/api/field-types): `registerFieldType` (alias: `registerWidget`)
* [Custom File Formats](/en/docs/api/file-formats): `registerCustomFormat`
* [Event Hooks](/en/docs/api/events): `registerEventListener`
::: warning Breaking changes from Netlify/Decap CMS
The methods other than those listed above are not supported in Sveltia CMS. This includes:
* `registerLocale`: Sveltia CMS automatically detects and uses the browser’s language settings for localization. No manual registration of locales is necessary.
* `registerRemarkPlugin`: Sveltia CMS uses the Lexical framework for Markdown processing instead of Remark. Therefore, Remark plugins are not compatible.
* All other undocumented methods, including custom backends and custom media storage providers. We may support these features in the future, but our implementation would likely be incompatible with Netlify/Decap CMS.
:::
---
---
url: /en/docs/frameworks/jekyll.md
description: 'Learn how to integrate Sveltia CMS with Jekyll, including real-world examples.'
---
# Jekyll Integration Guide
This guide provides resources and information for integrating Sveltia CMS with [Jekyll](https://jekyllrb.com/), a popular static site generator.
## Examples
See real-world examples of Jekyll integrations in our [Showcase](/en/showcase?framework=jekyll). Most of the listed sites include links to their source code, so you can explore how they implemented Sveltia CMS with Jekyll.
## Support for Jekyll
We have implemented specific features to enhance the integration of Sveltia CMS with Jekyll:
* [Localizing entry slugs](/en/docs/i18n#localizing-entry-slugs): generate localized slugs for multilingual Jekyll sites.
## Development Guide
We’ll be adding a detailed development guide for integrating Sveltia CMS with Jekyll in the near future. In the meantime, you can refer to the [Decap CMS documentation](https://decapcms.org/docs/jekyll/), as the basic concepts are similar.
---
---
url: /en/docs/fields/keyvalue.md
description: >-
Create and manage dynamic key-value pairs in Sveltia CMS for flexible data
structures.
---
# KeyValue Field
The KeyValue field type allows users to create and manage a dynamic list of key-value pairs, or dictionary entries, within the CMS entry form.
## User Interface
### Editor
A dynamic list of key-value pairs, where users can add, edit, and remove entries. Each entry consists of a text input for the key and a text input for the value.
You can press Enter to move focus or add a new row while editing.
### Preview
A table displaying the current key-value pairs in a structured format for easy review.
## Data Type
An object where each key corresponds to a user-defined key and each value corresponds to the associated value.
If the `required` option is set to `false` and the field is left empty, the value will be an empty object.
## Data Validation
* If the `required` option is set to `true`, at least one key-value pair must be present.
* Keys must be unique and non-empty strings. Keys cannot contain dots (`.`) as they may interfere with nested data structures.
* If `min` and/or `max` options are specified, the number of key-value pairs must be within the defined limits.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the KeyValue field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `keyvalue`.
### Optional Options
#### `default`
* **Type**: `object`
* **Default**: `{}`
The default value for the field when creating a new entry.
#### `key_label`
* **Type**: `string`
* **Default**: `"Key"` or its localized equivalent
The label for the key input field.
#### `value_label`
* **Type**: `string`
* **Default**: `"Value"` or its localized equivalent
The label for the value input field.
#### `min`
* **Type**: `integer`
* **Default**: `0`
The minimum number of key-value pairs required. This enables validation to ensure that users add at least this many entries.
#### `max`
* **Type**: `integer`
* **Default**: `Infinity`
The maximum number of key-value pairs allowed. This enables validation to prevent users from adding more than this many entries.
#### `root`
* **Type**: `boolean`
* **Default**: `false`
If set to `true`, the key-value pairs will be stored at the root level of the entry data instead of nested under the field name. This is similar to how the [`root` option for the List field](/en/docs/fields/list#root) works. The option is ignored if the file or singleton contains multiple fields.
See the [Top-Level key-value pairs](#top-level-key-value-pairs) example below for details.
## Examples
### Basic Key-Value Field
This example demonstrates a simple KeyValue field configuration:
::: code-group
```yaml [YAML]
- name: settings
label: Settings
widget: keyvalue
```
```toml [TOML]
[[fields]]
name = "settings"
label = "Settings"
widget = "keyvalue"
```
```json [JSON]
{
"name": "settings",
"label": "Settings",
"widget": "keyvalue"
}
```
```js [JavaScript]
{
name: 'settings',
label: 'Settings',
widget: 'keyvalue',
}
```
:::
Output example:
::: code-group
```yaml [YAML]
settings:
theme: dark
notifications: enabled
```
```toml [TOML]
[settings]
theme = "dark"
notifications = "enabled"
```
```json [JSON]
{
"settings": {
"theme": "dark",
"notifications": "enabled"
}
}
```
:::
### Top-Level Key-Value Pairs
This example demonstrates how to use the `root` option to store key-value pairs at the root level of the entry data:
::: code-group
```yaml{4} [YAML]
- name: settings
label: Settings
widget: keyvalue
root: true
```
```toml{5} [TOML]
[[fields]]
name = "settings"
label = "Settings"
widget = "keyvalue"
root = true
```
```json{5} [JSON]
{
"name": "settings",
"label": "Settings",
"widget": "keyvalue",
"root": true
}
```
```js{5} [JavaScript]
{
name: 'settings',
label: 'Settings',
widget: 'keyvalue',
root: true,
}
```
:::
Output example:
::: code-group
```yaml [YAML]
theme: dark
notifications: enabled
```
```toml [TOML]
theme = "dark"
notifications = "enabled"
```
```json [JSON]
{
"theme": "dark",
"notifications": "enabled"
}
```
:::
---
---
url: /en/docs/fields/list.md
description: >-
Create and manage lists in Sveltia CMS with flexible item types and nested
support.
---
# List Field
The List field type allows users to create and manage lists of items within the CMS entry form. It supports various configurations for defining the structure and type of items in the list, either as a simple array or as a list of complex objects.
## User Interface
### Editor
The List field type has four different UI modes, depending on the configuration:
* Complex list field:
* With the `field` option: A single subfield editor is shown for each item in the list.
* With the `fields` option: A group of subfield editors is shown for each item in the list.
* With the `types` option: A type selector is shown for each item, along with the corresponding subfield editors. This configuration is called a **variable type** list. It’s useful for creating flexible content structures like page builders.
* Simple list field:
* Without the `field`, `fields` or `types` option: A simple multiline text area is shown for editing string values. This allows users to enter list items separated by new lines, where spaces and commas are treated as part of the item values instead of delimiters.
With a complex list field:
* Each item in the list can be expanded or collapsed to show or hide its subfields.
* Each item comes with a menu that allows users to duplicate the item, insert a new item above/below it, or remove it.
* Users can expand or collapse the entire list using the Expand All and Collapse All buttons.
::: info Future Plans
The UI will be improved in the future to support additional features like drag-and-drop reordering, inline editing, and better handling of large lists.
:::
### Preview
A list view displaying all items in the list. For complex list fields, grouped subfield values are shown for each item. For simple list fields, a bulleted list of string values is displayed.
## Data Type
An array. The elements can be strings or objects, depending on the configuration.
If the `required` option is set to `false` and the field is left empty, the value will be an empty array.
## Data Validation
* If the `required` option is set to `true`, the list must contain at least one item.
* If the `min` and/or `max` options are specified, the number of items in the list must be within the defined limits.
* Each item in the list is validated according to the subfield definitions, if applicable.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the List field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `list` to use the List field type.
### General Options
#### `default`
### Subfield Definition
These options are mutually exclusive; you can only use one of them at a time:
#### `field`
* **Type**: A single [field definition](/en/docs/fields)
#### `fields`
* **Type**: `array` of [field definitions](/en/docs/fields)
#### `types`
* **Type**: `array` of variable type definitions
Each type definition is an object with the following properties:
* `name` (string, required): The unique identifier for the type.
* `label` (string, required): The display label for the type.
* `widget` (string, optional): The field type for this type. It must be `object` if not omitted. Other field types are invalid.
* `fields` (array of field definitions, optional): The subfields for this type.
### Subfield Options
These options are effective only when the `field`, `fields`, or `types` option is used:
#### `root`
* **Type**: `boolean`
* **Default**: `false`
Whether to store the list at the root level of the output file, without a parent key. This is useful for creating top-level lists in files.
The `root` option is ignored in the following cases:
* The file or singleton contains multiple fields. You can still have subfields under the List field.
* The file format is TOML, because TOML doesn’t support top-level arrays.
See the [Top-Level List](#top-level-list) example below for details.
#### `label_singular`
* **Type**: `string`
* **Default**: The value of the `label` option
A label used for singular items in the list, e.g., "Member" for a list labeled "Members". It will be displayed on the Add button and in other relevant places in the UI.
#### `summary`
* **Type**: `string`
* **Default**: `""`
A template string used to generate a summary for each item in the collapsed view. It can include subfield values using the `{{subfield_name}}` syntax. [String transformations](/en/docs/string-transformations) can be applied in this option. If omitted, the summary will be automatically generated based on the first textual subfield found.
See the [Using Summary and Thumbnail](#using-summary-and-thumbnail) example below for details.
#### `thumbnail`
* **Type**: `string`
* **Default**: `""`
An Image subfield name to be used as the thumbnail for each list item in the collapsed view, if applicable. If omitted, no thumbnail will be displayed.
See the [Using Summary and Thumbnail](#using-summary-and-thumbnail) example below for details.
#### `collapsed`
* **Type**: `boolean` or `auto`
* **Default**: `false`
Whether each item is initially collapsed in the UI. If set to `auto`, the UI is collapsed if an item has any filled subfields and expanded if all the subfields are empty.
#### `minimize_collapsed`
* **Type**: `boolean` or `auto`
* **Default**: `false`
Whether the entire list is minimized when collapsed. If set to `auto`, the list is minimized if any item has any filled subfields and expanded if all items are empty.
#### `allow_add`
* **Type**: `boolean`
* **Default**: `true`
Whether to allow adding new items to the list. If set to `false`, the Add button will be hidden.
#### `allow_remove`
* **Type**: `boolean`
* **Default**: `true`
Whether to allow removing items from the list. If set to `false`, the Remove button will be hidden.
#### `allow_reorder`
* **Type**: `boolean`
* **Default**: `true`
Whether to allow reordering of items in the list with an arrow handle or drag-and-drop. If set to `false`, the reorder handles will be hidden.
#### `min`
* **Type**: `integer`
* **Default**: `0`
The minimum number of items required in the list. If the number of items is below this value, a validation error will be shown.
#### `max`
* **Type**: `integer`
* **Default**: `Infinity`
The maximum number of items allowed in the list. If the number of items exceeds this value, a validation error will be shown.
#### `add_to_top`
* **Type**: `boolean`
* **Default**: `false`
Whether to add new items to the top of the list instead of the bottom. If set to `true`, the Add button will appear at the top of the list.
#### `typeKey`
* **Type**: `string`
* **Default**: `type`
This option is effective only when the `types` option is used. It allows you to customize the name of the field that indicates the type of each item in the list. See the [Variable Type](#variable-type) example below for details.
You cannot use a key that conflicts with any of the subfield names defined in the object.
::: tip
Unlike most of other config options, `typeKey` is camelCased.
:::
## Examples
### Simple List
Configuration example:
::: code-group
```yaml [YAML]
- name: tags
label: Tags
widget: list
```
```toml [TOML]
[[fields]]
name = "tags"
label = "Tags"
widget = "list"
```
```json [JSON]
{
"name": "tags",
"label": "Tags",
"widget": "list"
}
```
```js [JavaScript]
{
name: "tags",
label: "Tags",
widget: "list",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
tags:
- travel
- photography
- food
```
```toml [TOML]
tags = ["travel", "photography", "food"]
```
```json [JSON]
{
"tags": ["travel", "photography", "food"]
}
```
:::
### Single Subfield
Configuration example:
::: code-group
```yaml{4} [YAML]
- name: authors
label: Authors
widget: list
field:
name: author
label: Author
widget: string
```
```toml [TOML]
[[fields]]
name = "authors"
label = "Authors"
widget = "list"
[field]
name = "author"
label = "Author"
widget = "string"
```
```json [JSON]
{
"name": "authors",
"label": "Authors",
"widget": "list",
"field": {
"name": "author",
"label": "Author",
"widget": "string"
}
}
```
```js [JavaScript]
{
name: "authors",
label: "Authors",
widget: "list",
field: {
name: "author",
label: "Author",
widget: "string",
},
}
```
:::
Output example:
::: code-group
```yaml [YAML]
authors:
- Alice
- Bob
- Charlie
```
```toml [TOML]
authors = ["Alice", "Bob", "Charlie"]
```
```json [JSON]
{
"authors": ["Alice", "Bob", "Charlie"]
}
```
:::
Note that the `name` of the subfield will not appear in the output; only the values will be included in the list, just like a simple list.
### Multiple Subfields
Configuration example:
::: code-group
```yaml{4} [YAML]
- name: team_members
label: Team Members
widget: list
fields:
- name: name
label: Name
widget: string
- name: role
label: Role
widget: string
```
```toml [TOML]
[[fields]]
name = "team_members"
label = "Team Members"
widget = "list"
[[fields.fields]]
name = "name"
label = "Name"
widget = "string"
[[fields.fields]]
name = "role"
label = "Role"
widget = "string"
```
```json [JSON]
{
"name": "team_members",
"label": "Team Members",
"widget": "list",
"fields": [
{
"name": "name",
"label": "Name",
"widget": "string"
},
{
"name": "role",
"label": "Role",
"widget": "string"
}
]
}
```
```js [JavaScript]
{
name: "team_members",
label: "Team Members",
widget: "list",
fields: [
{
name: "name",
label: "Name",
widget: "string",
},
{
name: "role",
label: "Role",
widget: "string",
},
],
}
```
:::
Output example:
::: code-group
```yaml [YAML]
team_members:
- name: Alice
role: Developer
- name: Bob
role: Designer
- name: Charlie
role: Product Manager
```
```toml [TOML]
[[team_members]]
name = "Alice"
role = "Developer"
[[team_members]]
name = "Bob"
role = "Designer"
[[team_members]]
name = "Charlie"
role = "Product Manager"
```
```json [JSON]
{
"team_members": [
{
"name": "Alice",
"role": "Developer"
},
{
"name": "Bob",
"role": "Designer"
},
{
"name": "Charlie",
"role": "Product Manager"
}
]
}
```
:::
### Using Summary and Thumbnail
Configuration example:
::: code-group
```yaml{4-5} [YAML]
- name: projects
label: Projects
widget: list
summary: "{{name}} - {{status}}"
thumbnail: "image"
fields:
- name: name
label: Name
widget: string
- name: status
label: Status
widget: string
- name: image
label: Image
widget: image
```
```toml{5-6} [TOML]
[[fields]]
name = "projects"
label = "Projects"
widget = "list"
summary = "{{name}} - {{status}}"
thumbnail = "image"
[[fields.fields]]
name = "name"
label = "Name"
widget = "string"
[[fields.fields]]
name = "status"
label = "Status"
widget = "string"
[[fields.fields]]
name = "image"
label = "Image"
widget = "image"
```
```json{5-6} [JSON]
{
"name": "projects",
"label": "Projects",
"widget": "list",
"summary": "{{name}} - {{status}}",
"thumbnail": "image",
"fields": [
{
"name": "name",
"label": "Name",
"widget": "string"
},
{
"name": "status",
"label": "Status",
"widget": "string"
},
{
"name": "image",
"label": "Image",
"widget": "image"
}
]
}
```
```js{5-6} [JavaScript]
{
name: "projects",
label: "Projects",
widget: "list",
summary: "{{name}} - {{status}}",
thumbnail: "image",
fields: [
{
name: "name",
label: "Name",
widget: "string",
},
{
name: "status",
label: "Status",
widget: "string",
},
{
name: "image",
label: "Image",
widget: "image",
},
],
}
```
:::
### Variable Type
The following example defines a variable type List field named `items` with two types: `text_item` and `image_item`. User can add either type of item to the list. These types can be mixed in any order.
::: code-group
```yaml{4} [YAML]
- name: items
label: Items
widget: list
types:
- name: text_item
label: Text Item
fields:
- name: text
label: Text
widget: string
- name: image_item
label: Image Item
fields:
- name: url
label: Image URL
widget: image
- name: caption
label: Caption
widget: string
```
```toml [TOML]
[[fields]]
name = "items"
label = "Items"
widget = "list"
[[fields.types]]
label = "Text Item"
name = "text_item"
[[fields.types.fields]]
name = "text"
label = "Text"
widget = "string"
[[fields.types]]
label = "Image Item"
name = "image_item"
[[fields.types.fields]]
name = "url"
label = "Image URL"
widget = "image"
[[fields.types.fields]]
name = "caption"
label = "Caption"
widget = "string"
```
```json [JSON]
{
"name": "items",
"label": "Items",
"widget": "list",
"types": [
{
"label": "Text Item",
"name": "text_item",
"fields": [
{
"name": "text",
"label": "Text",
"widget": "string"
}
]
},
{
"label": "Image Item",
"name": "image_item",
"fields": [
{
"name": "url",
"label": "Image URL",
"widget": "image"
},
{
"name": "caption",
"label": "Caption",
"widget": "string"
}
]
}
]
}
```
```js [JavaScript]
{
name: "items",
label: "Items",
widget: "list",
types: [
{
label: "Text Item",
name: "text_item",
fields: [
{
name: "text",
label: "Text",
widget: "string",
},
],
},
{
label: "Image Item",
name: "image_item",
fields: [
{
name: "url",
label: "Image URL",
widget: "image",
},
{
name: "caption",
label: "Caption",
widget: "string",
},
],
},
],
}
```
:::
Output example:
::: code-group
```yaml [YAML]
items:
- type: text_item
text: This is a text item.
- type: image_item
url: https://example.com/image.jpg
caption: An example image.
- type: text_item
text: Another text item.
```
```toml [TOML]
[[items]]
type = "text_item"
text = "This is a text item."
[[items]]
type = "image_item"
url = "https://example.com/image.jpg"
caption = "An example image."
[[items]]
type = "text_item"
text = "Another text item."
```
```json [JSON]
{
"items": [
{
"type": "text_item",
"text": "This is a text item."
},
{
"type": "image_item",
"url": "https://example.com/image.jpg",
"caption": "An example image."
},
{
"type": "text_item",
"text": "Another text item."
}
]
}
```
:::
### Variable Type with Nested List
The following example defines a variable type List field named `sections` with two types: `text_section` and `image_gallery`. The `image_gallery` type contains a nested List field for multiple images.
::: tip
You cannot have a List field directly under the `types` option; it must be nested within a type Object field, as shown in this example.
:::
::: code-group
```yaml{4} [YAML]
- name: sections
label: Sections
widget: list
types:
- name: text_section
label: Text Section
fields:
- name: heading
label: Heading
widget: string
- name: body
label: Body
widget: text
- name: image_gallery
label: Image Gallery
fields:
- name: title
label: Title
widget: string
- name: images
label: Images
widget: list
fields:
- name: src
label: Image URL
widget: image
- name: alt
label: Alt Text
widget: string
```
```toml [TOML]
[[fields]]
name = "sections"
label = "Sections"
widget = "list"
[[fields.types]]
name = "text_section"
label = "Text Section"
[[fields.types.fields]]
name = "heading"
label = "Heading"
widget = "string"
[[fields.types.fields]]
name = "body"
label = "Body"
widget = "text"
[[fields.types]]
name = "image_gallery"
label = "Image Gallery"
[[fields.types.fields]]
name = "title"
label = "Title"
widget = "string"
[[fields.types.fields]]
name = "images"
label = "Images"
widget = "list"
[[fields.types.fields.fields]]
name = "src"
label = "Image URL"
widget = "image"
[[fields.types.fields.fields]]
name = "alt"
label = "Alt Text"
widget = "string"
```
```json [JSON]
{
"name": "sections",
"label": "Sections",
"widget": "list",
"types": [
{
"name": "text_section",
"label": "Text Section",
"fields": [
{
"name": "heading",
"label": "Heading",
"widget": "string"
},
{
"name": "body",
"label": "Body",
"widget": "text"
}
]
},
{
"name": "image_gallery",
"label": "Image Gallery",
"fields": [
{
"name": "title",
"label": "Title",
"widget": "string"
},
{
"name": "images",
"label": "Images",
"widget": "list",
"fields": [
{
"name": "src",
"label": "Image URL",
"widget": "image"
},
{
"name": "alt",
"label": "Alt Text",
"widget": "string"
}
]
}
]
}
]
}
```
```js [JavaScript]
{
name: "sections",
label: "Sections",
widget: "list",
types: [
{
name: "text_section",
label: "Text Section",
fields: [
{
name: "heading",
label: "Heading",
widget: "string",
},
{
name: "body",
label: "Body",
widget: "text",
},
],
},
{
name: "image_gallery",
label: "Image Gallery",
fields: [
{
name: "title",
label: "Title",
widget: "string",
},
{
name: "images",
label: "Images",
widget: "list",
fields: [
{
name: "src",
label: "Image URL",
widget: "image",
},
{
name: "alt",
label: "Alt Text",
widget: "string",
},
],
},
],
},
],
}
```
:::
Output example:
::: code-group
```yaml [YAML]
sections:
- type: text_section
heading: Welcome to Our Site
body: This is the first section of our site.
- type: image_gallery
title: Our Gallery
images:
- src: https://example.com/image1.jpg
alt: Image 1
- src: https://example.com/image2.jpg
alt: Image 2
```
```toml [TOML]
[[sections]]
type = "text_section"
heading = "Welcome to Our Site"
body = "This is the first section of our site."
[[sections]]
type = "image_gallery"
title = "Our Gallery"
[[sections.images]]
src = "https://example.com/image1.jpg"
alt = "Image 1"
[[sections.images]]
src = "https://example.com/image2.jpg"
alt = "Image 2"
```
```json [JSON]
{
"sections": [
{
"type": "text_section",
"heading": "Welcome to Our Site",
"body": "This is the first section of our site."
},
{
"type": "image_gallery",
"title": "Our Gallery",
"images": [
{
"src": "https://example.com/image1.jpg",
"alt": "Image 1"
},
{
"src": "https://example.com/image2.jpg",
"alt": "Image 2"
}
]
}
]
}
```
:::
### Variable Type with Custom Type Key
By default, the type field is named `type`, but you can customize it using the `typeKey` option. Also, the `fields` option can be omitted if a type has no subfields.
The following example shows a simple page builder configuration with three block types: Heading, Paragraph, and Horizontal Rule.
::: code-group
```yaml{4} [YAML]
- name: blocks
label: Blocks
widget: list
typeKey: tag
types:
- name: h2
label: Heading
fields:
- name: text
label: Text
widget: string
- name: p
label: Paragraph
fields:
- name: text
label: Text
widget: string
- name: hr
label: Horizontal Rule
```
```toml [TOML]
[[fields]]
name = "blocks"
label = "Blocks"
widget = "list"
typeKey = "tag"
[[fields.types]]
name = "h2"
label = "Heading"
[[fields.types.fields]]
name = "text"
label = "Text"
widget = "string"
[[fields.types]]
name = "p"
label = "Paragraph"
[[fields.types.fields]]
name = "text"
label = "Text"
widget = "string"
[[fields.types]]
name = "hr"
label = "Horizontal Rule"
```
```json [JSON]
{
"name": "blocks",
"label": "Blocks",
"widget": "list",
"typeKey": "tag",
"types": [
{
"name": "h2",
"label": "Heading",
"fields": [
{
"name": "text",
"label": "Text",
"widget": "string"
}
]
},
{
"name": "p",
"label": "Paragraph",
"fields": [
{
"name": "text",
"label": "Text",
"widget": "string"
}
]
},
{
"name": "hr",
"label": "Horizontal Rule"
}
]
}
```
```js [JavaScript]
{
name: "blocks",
label: "Blocks",
widget: "list",
typeKey: "tag",
types: [
{
name: "h2",
label: "Heading",
fields: [
{
name: "text",
label: "Text",
widget: "string",
},
],
},
{
name: "p",
label: "Paragraph",
fields: [
{
name: "text",
label: "Text",
widget: "string",
},
],
},
{
name: "hr",
label: "Horizontal Rule",
},
],
}
```
:::
Output example:
::: code-group
```yaml [YAML]
blocks:
- tag: h2
text: Welcome to Our Site
- tag: p
text: This is the first paragraph of the site.
- tag: hr
- tag: p
text: This is another paragraph after the horizontal rule.
```
```toml [TOML]
[[blocks]]
tag = "h2"
text = "Welcome to Our Site"
[[blocks]]
tag = "p"
text = "This is the first paragraph of the site."
[[blocks]]
tag = "hr"
[[blocks]]
tag = "p"
text = "This is another paragraph after the horizontal rule."
```
```json [JSON]
{
"blocks": [
{
"tag": "h2",
"text": "Welcome to Our Site"
},
{
"tag": "p",
"text": "This is the first paragraph of the site."
},
{
"tag": "hr"
},
{
"tag": "p",
"text": "This is another paragraph after the horizontal rule."
}
]
}
```
:::
### Top-Level List
It’s possible to define a List field at the top level of an output file, using the `root` option. The configuration below reproduces [this Jekyll data file example](https://jekyllrb.com/docs/datafiles/#example-list-of-members):
::: code-group
```yaml{14} [YAML]
collections:
- name: data
label: Data Files
files:
- name: members
label: Member List
file: _data/members.yml
icon: group
fields:
- name: members
label: Members
label_singular: Member
widget: list
root: true
fields:
- name: name
label: Name
- name: github
label: GitHub account
```
```toml{14} [TOML]
[[collections]]
name = "data"
label = "Data Files"
[[collections.files]]
name = "members"
label = "Member List"
file = "_data/members.yml"
icon = "group"
[[collections.files.fields]]
name = "members"
label = "Members"
label_singular = "Member"
widget = "list"
root = true
[[collections.files.fields.fields]]
name = "name"
label = "Name"
[[collections.files.fields.fields]]
name = "github"
label = "GitHub account"
```
```json{18} [JSON]
{
"collections": [
{
"name": "data",
"label": "Data Files",
"files": [
{
"name": "members",
"label": "Member List",
"file": "_data/members.yml",
"icon": "group",
"fields": [
{
"name": "members",
"label": "Members",
"label_singular": "Member",
"widget": "list",
"root": true,
"fields": [
{
"name": "name",
"label": "Name"
},
{
"name": "github",
"label": "GitHub account"
}
]
}
]
}
]
}
]
}
```
```js{18} [JavaScript]
{
collections: [
{
name: "data",
label: "Data Files",
files: [
{
name: "members",
label: "Member List",
file: "_data/members.yml",
icon: "group",
fields: [
{
name: "members",
label: "Members",
label_singular: "Member",
widget: "list",
root: true,
fields: [
{
name: "name",
label: "Name",
},
{
name: "github",
label: "GitHub account",
},
],
},
],
},
],
},
],
}
```
:::
It also works with a [singleton](/en/docs/collections/singletons). The configuration below reproduces the same data file example using a singleton:
::: code-group
```yaml{11} [YAML]
singletons:
- name: members
label: Member List
file: _data/members.yml
icon: group
fields:
- name: members
label: Members
label_singular: Member
widget: list
root: true
fields:
- name: name
label: Name
- name: github
label: GitHub account
```
```toml{11} [TOML]
[[singletons]]
name = "members"
label = "Member List"
file = "_data/members.yml"
icon = "group"
[[singletons.fields]]
name = "members"
label = "Members"
label_singular = "Member"
widget = "list"
root = true
[[singletons.fields.fields]]
name = "name"
label = "Name"
[[singletons.fields.fields]]
name = "github"
label = "GitHub account"
```
```json{14} [JSON]
{
"singletons": [
{
"name": "members",
"label": "Member List",
"file": "_data/members.yml",
"icon": "group",
"fields": [
{
"name": "members",
"label": "Members",
"label_singular": "Member",
"widget": "list",
"root": true,
"fields": [
{
"name": "name",
"label": "Name"
},
{
"name": "github",
"label": "GitHub account"
}
]
}
]
}
]
}
```
```js{14} [JavaScript]
{
singletons: [
{
name: "members",
label: "Member List",
file: "_data/members.yml",
icon: "group",
fields: [
{
name: "members",
label: "Members",
label_singular: "Member",
widget: "list",
root: true,
fields: [
{
name: "name",
label: "Name",
},
{
name: "github",
label: "GitHub account",
},
],
},
],
},
],
}
```
:::
Output example:
::: code-group
```yaml [YAML]
- name: Alice
github: alicehub123
- name: Bob
github: bobgit456
- name: Charlie
github: charliecode789
```
```json [JSON]
[
{
"name": "Alice",
"github": "alicehub123"
},
{
"name": "Bob",
"github": "bobgit456"
},
{
"name": "Charlie",
"github": "charliecode789"
}
]
```
:::
As you can see, the list is stored directly at the root level of the output file, without a parent key (`members`). We don’t have a TOML example here because TOML format cannot represent top-level arrays; thus, the `root` option is ignored for TOML files.
---
---
url: /en/docs/workflows/local.md
description: Work with local Git repositories using Sveltia CMS on a development server.
---
# Local Workflow
Developers can work with a local Git repository using Sveltia CMS while running it on a local development server. This allows you to test and edit your content locally without needing to push changes to a remote repository first.
## Use Cases
* Test Sveltia CMS locally before deploying it to a production environment.
* Edit the CMS configuration and see how it affects the CMS behavior.
* Make bulk changes to content files and assets and commit them at once.
* Work offline without an internet connection.
## Requirements
You must have a Git repository initialized in your project directory. You can create a new repository with [`git init`](https://github.com/git-guides/git-init) or clone an existing one.
You also need to have a local development server running for your frontend framework (e.g., Astro, Eleventy, Hugo, SvelteKit) and have installed Sveltia CMS in the project.
You need Google Chrome, Microsoft Edge, Brave, or any other Chromium-based browser. The workflow doesn’t work in Firefox, Safari, or other non-Chromium browsers, because this feature relies on the [File System Access API](https://developer.chrome.com/docs/capabilities/web-apis/file-system-access), which is only supported by Chromium-based browsers at this time.
### Enabling File System Access API in Brave
In the Brave browser, you must manually enable the File System Access API with an experiment flag to take advantage of the local repository workflow.
1. Open `brave://flags/#file-system-access-api` in a new browser tab.
2. Click Default (Disabled) next to File System Access API and select Enabled.
3. Relaunch the browser.
## Configuration
In your CMS configuration, you must configure one of the supported Git backends: [GitHub](/en/docs/backends/github), [GitLab](/en/docs/backends/gitlab) or [Gitea/Forgejo](/en/docs/backends/gitea-forgejo). No other configuration is required.
::: tip Authentication Not Required
If you plan to only work with your local repository, you don’t need to set up authentication with your Git backend. You can use the CMS as a local-only editor UI and commit changes manually using Git. However, if you want to edit content remotely as well, you must set up authentication as described in the backend documentation.
:::
::: tip Repository Name Can Be Arbitrary
If you don’t have a remote repository yet, you can use any repository name for the `repo` property in the backend configuration. The CMS doesn’t perform any Git operations, so it doesn’t matter if the repository actually exists or not. However, the backend configuration is still used to store data in the browser’s IndexedDB, which is partitioned by the backend `name` and `repo`. For this purpose, you can use a dummy name, such as `my-name/travel-blog`.
:::
## Workflow
The local workflow consists of four main steps:
### 1. Start the development server
Launch the local development server for your frontend framework, typically with `npm run dev`, `pnpm dev` or `yarn dev`.
### 2. Edit content
In any Chromium-based browser:
1. Open `http://localhost:[port]/admin/index.html`. Replace `[port]` with the actual port number used by your development server.
2. Click “Work with Local Repository” and select the project’s root directory once prompted.
3. Edit your content normally using the CMS. All changes are made to local files.
### 3. Preview changes
Open the dev site at `http://localhost:[port]/` in any browser to preview the rendered pages. To make further edits, return to the CMS.
### 4. Commit changes
With any Git client (CUI or GUI):
1. See if the produced changes (diff) look good.
2. Commit and push the changes if satisfied, or discard them if you’re just testing.
## Tips & Tricks
* An indicator is displayed in the Account menu when using the local workflow.
* The `localhost` URL:
* The port number varies by framework. Check the terminal output from the previous step. For example, if you use Vite-based frameworks like SvelteKit or VitePress, the default port is `5173`. Astro uses `4321`, Eleventy uses `8080`, Hugo uses `1313`, and Jekyll uses `4000`.
* The `127.0.0.1` addresses can also be used instead of `localhost`.
* If your CMS instance is not located under `/admin/`, use the appropriate path.
* It’s recommended to use `index.html` in the URL to make sure the framework treats it as a static file. For example, use `http://localhost:5173/admin/index.html` instead of `http://localhost:5173/admin/`.
* Git clients:
* You can use any Git client of your choice, including command-line tools (CUI) or graphical user interfaces (GUI).
* For CUI, you can use the standard Git commands like `git diff`, `git commit`, and `git push`.
* For GUI, popular options include [GitHub Desktop](https://github.com/apps/desktop), [Sourcetree](https://www.sourcetreeapp.com/), [Tower](https://www.git-tower.com/), and [GitKraken](https://www.gitkraken.com/). GitHub Desktop can be used for any repository, not just GitHub-hosted ones. [VS Code](https://code.visualstudio.com/docs/sourcecontrol/overview) also has built-in Git support.
* Depending on your framework, you may need to manually rebuild your site or reload the page to reflect the changes you have made. Check your framework’s documentation for details.
* You can skip the site preview check if your changes don’t involve any pages.
## Troubleshooting
* If you get an error saying “not a repository root directory”, make sure you’ve turned the folder into a repository with either a CUI ([`git init`](https://github.com/git-guides/git-init)) or GUI, and the hidden `.git` folder exists. While Sveltia CMS doesn’t read/write files inside the `.git` folder, it checks for the presence of the `.git` folder to verify that the selected folder is the project root and make sure changes made in the CMS can be tracked by Git.
* If you’re using Windows Subsystem for Linux (WSL), you may get an error saying “Can’t open this folder because it contains system files.” This is due to a limitation in the browser, and you can try some workarounds mentioned in [this issue](https://github.com/coder/code-server/issues/4646) and [this thread](https://github.com/sveltia/sveltia-cms/discussions/101).
## Limitations
The local repository support in Sveltia CMS doesn’t perform any Git operations. You have to manually fetch, pull, commit and push all changes using a Git client. Additionally, you’ll need to reload the CMS after modifying the configuration file or retrieving remote updates.
::: info Future Plans
We will explore possibilities to add built-in Git operations in the CMS itself, possibly by integrating [isomorphic-git](https://isomorphic-git.org/), to enable committing changes directly from the CMS interface. The Netlify/Decap CMS proxy server actually has an experimental, undocumented Git mode that create commits locally. For more details, see discussion [#31](https://github.com/sveltia/sveltia-cms/discussions/31).
We also plan to use the newly available [File System Observer API](https://developer.chrome.com/blog/file-system-observer) to detect changes and eliminate the need for manual reloads.
:::
---
---
url: /en/docs/api/initialization.md
description: >-
Manually initialize Sveltia CMS with the init function for greater control
over CMS startup.
---
# Manual Initialization
By default, Sveltia CMS automatically initializes itself when the script is loaded. However, in some cases, you may want to have more control over when and how the CMS is initialized. This is where the `init` function comes into play.
## Overview
To manually initialize the CMS, call the `init` function on the [`CMS` object](/en/docs/api#accessing-the-cms-object):
```js
CMS.init({ config });
```
### Parameters
* `config` (optional): An object that can contain any of the configuration options available in the `config.yml` file. If provided, this configuration will be merged with the one loaded from `config.yml` (if the `load_config_file` option is `true` or omitted) or used directly (if `load_config_file` is `false`).
::: tip Config File Loading Behavior
Unless you set the `load_config_file` option to `false`, the CMS will always attempt to load the `config.yml` file, even when you provide a configuration object, and raise an error if the file is not found or cannot be loaded. If you want to completely bypass loading the configuration file, make sure to set this option accordingly.
:::
## Usage Notes
### Preventing Automatic Initialization
If you use the UNPKG CDN, you have to set a global variable `CMS_MANUAL_INIT` to `true` before loading the script to prevent automatic initialization.
```html
```
For NPM installations, you don’t need this step; manual initialization is the default behavior. In other words, you always have to call `init()` yourself.
### Typing the Configuration Object
The `CMS` object is typed, so if you are using TypeScript, you will get type checking and autocompletion when providing the configuration object to the `init` function.
You can also import the `CmsConfig` type from the `@sveltia/cms` package to type the configuration object if you construct it outside of the `init` call. Here’s an example:
```ts
import { init, type CmsConfig } from '@sveltia/cms';
const config: CmsConfig = {
// your config here
};
init({ config });
```
::: info Experimental Types
Types other than `CmsConfig` can also be imported for more specific parts of the configuration, such as `GitHubBackend`, `EntryCollection`, `DateTimeField`, etc. However, this is experimental and subject to change, so it’s recommended to use `CmsConfig` for now.
:::
## Examples
### Initializing the CMS Normally
This will load the configuration from `config.yml` and initialize the CMS as usual, just like the automatic initialization.
```js
CMS.init();
```
### Providing a Full Configuration
When the `load_config_file` option is set to `false`, the configuration provided here will be used directly, and the `config.yml` file will not be loaded. Make sure to include all required options: `backend`, `media_folder` and `collections`.
```js {3}
CMS.init({
config: {
load_config_file: false,
backend: {
name: 'github',
repo: 'user/repo',
},
media_folder: '/static/media',
public_folder: '/media',
collections: [
// your collections here
],
},
});
```
### Providing a Partial Configuration
If the `load_config_file` option is set to `true` or omitted, the configuration provided here will be merged with the one loaded from `config.yml` using the [`deepmerge`](https://www.npmjs.com/package/deepmerge) library, so you can override or add specific settings. Use cases for this are more limited, but it can be useful in some scenarios.
For example, you could override the [backend branch](/en/docs/backends#branch-selection) like this:
```js
CMS.init({
config: {
backend: {
branch: 'development',
},
},
});
```
---
---
url: /en/docs/fields/map.md
description: >-
Select geographic locations in Sveltia CMS with an interactive map and
geolocation support.
---
# Map Field
The Map field type allows users to select geographic locations using an interactive map interface. It supports selecting single points, lines, or polygons, and stores the selected geometry as a GeoJSON string.
## User Interface
### Editor
An interactive map interface that enables users to select geographic locations visually by clicking on the map. The map supports zooming and panning for better precision. It also includes the following features:
* A search box to find locations by name or address.
* A button to center the map on the user’s current location using the browser’s [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API).
* A Clear button to remove the selected location(s).
The map UI is built with the [Leaflet](https://leafletjs.com/) and [Terra Draw](https://github.com/JamesLMilner/terra-draw) libraries, utilizing [OpenStreetMap](https://www.openstreetmap.org/) tiles and the [Nominatim](https://nominatim.org/) search API. You don’t need to set up any API keys to use those free services.
::: tip CSP Settings
You may need to update your Content Security Policy (CSP) to allow loading map tiles and making search API requests. See the [CSP documentation](/en/docs/security#setting-up-content-security-policy) for more details.
:::
::: info Future Plans
We plan to add support for additional map providers in the future, such as Mapbox and Google Maps, to offer more customization options.
:::
### Preview
The data output, which is a GeoJSON string, is displayed. See below for details on the data format.
## Data Type
A stringified [GeoJSON](https://geojson.org/) object representing the selected geometry. Depending on the selected `type` option, the GeoJSON will be in one of the following formats:
```
{"type":"Point","coordinates":[lng,lat]}
```
```
{"type":"LineString","coordinates":[[lng1,lat1],[lng2,lat2],...]}
```
```
{"type":"Polygon","coordinates":[[[lng1,lat1],[lng2,lat2],[lng3,lat3],...]]}
```
You need to parse this string to work with the GeoJSON data in your application.
::: tip Coordinate Order
The coordinates are in the order of longitude first, then latitude, as per the GeoJSON spec. Some libraries use latitude-longitude order, so be cautious when integrating with other mapping tools.
:::
If the `required` option is set to `false` and locations are not selected, the value will be an empty string.
## Data Validation
* If the `required` option is set to `true`, a valid GeoJSON string must be provided.
* The GeoJSON string must conform to the specified geometry `type` (Point, LineString, or Polygon).
* Coordinates must be valid latitude and longitude values.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the Map field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `map`.
### Optional Options
#### `default`
* **Type**: `string`
* **Default**: `""`
A default GeoJSON string to prepopulate the field. The string should be a valid GeoJSON representation of the selected geometry. The `type` option should match the geometry type of the default value.
#### `decimals`
* **Type**: `number`
* **Default**: `7`
Number of decimal places for coordinates. Higher values provide more precision but may increase the size of the stored data.
#### `type`
* **Type**: `string`
* **Default**: `Point`
Type of geometry to select. Supported values are:
* `Point`: Allows selecting a single location on the map.
* `LineString`: Allows drawing a line by selecting multiple points.
* `Polygon`: Allows drawing a polygon by selecting multiple points that form a closed shape.
## Examples
### Basic Map Field
This example shows a simple Map field configuration, which allows users to select a single location on the map.
::: code-group
```yaml [YAML]
- name: location
label: Location
widget: map
```
```toml [TOML]
[[fields]]
name = "location"
label = "Location"
widget = "map"
```
```json [JSON]
{
"name": "location",
"label": "Location",
"widget": "map"
}
```
```js [JavaScript]
{
name: 'location',
label: 'Location',
widget: 'map',
}
```
:::
Output example:
::: code-group
```yaml [YAML]
location: '{"type":"Point","coordinates":[-122.4194015,37.7749144]}'
```
```toml [TOML]
location = '{"type":"Point","coordinates":[-122.4194015,37.7749144]}'
```
```json [JSON]
{
"location": "{\"type\":\"Point\",\"coordinates\":[-122.4194015,37.7749144]}"
}
```
:::
### LineString Map Field with Decimals
This example shows a Map field configured to select a LineString geometry with a specified number of decimal places for coordinates.
::: code-group
```yaml [YAML]
- name: route
label: Route
widget: map
type: LineString
decimals: 5
```
```toml [TOML]
[[fields]]
name = "route"
label = "Route"
widget = "map"
type = "LineString"
decimals = 5
```
```json [JSON]
{
"name": "route",
"label": "Route",
"widget": "map",
"type": "LineString",
"decimals": 5
}
```
```js [JavaScript]
{
name: 'route',
label: 'Route',
widget: 'map',
type: 'LineString',
decimals: 5,
}
```
:::
Output example:
::: code-group
```yaml [YAML]
route: '{"type":"LineString","coordinates":[[-122.41940,37.77490],[-122.41800,37.77550]]}'
```
```toml [TOML]
route = '{"type":"LineString","coordinates":[[-122.41940,37.77490],[-122.41800,37.77550]]}'
```
```json [JSON]
{
"route": "{\"type\":\"LineString\",\"coordinates\":[[-122.41940,37.77490],[-122.41800,37.77550]]}"
}
```
:::
---
---
url: /en/docs/fields/markdown.md
description: >-
Create and format content in Sveltia CMS with Markdown syntax and rich text
editing.
---
# Markdown Field
The Markdown field type is an alias of the RichText field type, available for backward compatibility with Netlify/Decap CMS. It provides a rich text editor that allows content editors to create and format content using Markdown syntax.
The `widget` property for this field type is `markdown`.
See the [RichText field documentation](/en/docs/fields/richtext) for details on the UI, data type, and available options.
## Examples
### Standard Markdown Field
This example shows a basic Markdown editor with default settings.
::: code-group
```yaml [YAML]
- name: body
label: Body
widget: markdown
```
```toml [TOML]
[[fields]]
name = "body"
label = "Body"
widget = "markdown"
```
```json [JSON]
{
"name": "body",
"label": "Body",
"widget": "markdown"
}
```
```js [JavaScript]
{
name: 'body',
label: 'Body',
widget: 'markdown',
}
```
:::
Output example:
::: code-group
```markdown [Markdown]
# Welcome to the Markdown Field
This is a sample paragraph in **Markdown** format.
- Item 1
- Item 2
```
```yaml [YAML]
body: |
# Welcome to the Markdown Field
This is a sample paragraph in **Markdown** format.
- Item 1
- Item 2
```
```toml [TOML]
body = """
# Welcome to the Markdown Field
This is a sample paragraph in **Markdown** format.
- Item 1
- Item 2
"""
```
```json [JSON]
{
"body": "# Welcome to the Markdown Field\n\nThis is a sample paragraph in **Markdown** format.\n\n- Item 1\n- Item 2\n"
}
```
:::
We have included a Markdown example output along with YAML, TOML, and JSON representations because a field named `body` with the Markdown field type would be stored outside of the frontmatter in a Markdown file. If the name of the field were different, the content would be stored in the frontmatter instead.
---
---
url: /en/docs/media.md
description: >-
Configure media storage in Sveltia CMS with internal Git storage and external
provider integrations.
---
# Media Storage
Sveltia CMS supports multiple media storage providers for managing media assets such as images and files. You can choose from the built-in internal media storage that saves files directly in your Git repository, or integrate with popular cloud-based media storage services for enhanced capabilities.
::: tip Note for Netlify/Decap CMS users
In Sveltia CMS, the term “media storage provider” is used instead of “media library” to avoid confusion with Sveltia CMS’s [Asset Library feature](/en/docs/ui/asset-library) that allows you to manage media assets from multiple sources in one place. There is no change in functionality or configuration; it’s simply a terminology update.
:::
## Internal Storage
The [internal media storage](/en/docs/media/internal) allows you to store media files directly in your Git repository along with your content files. It supports various configuration options for organizing and managing media files effectively.
## External Storage
Sveltia CMS supports integrations with popular cloud-based media storage providers for enhanced capabilities such as automatic image transformations, CDN delivery, and more. Sveltia CMS currently supports the following external media storage providers:
* [Cloudinary](/en/docs/media/cloudinary)
* [Uploadcare](/en/docs/media/uploadcare)
Unlike backends, you can use multiple storage providers simultaneously in Sveltia CMS. Each media storage provider integration includes its own configuration instructions.
::: warning Breaking changes from Netlify/Decap CMS
Sveltia CMS does not support the deprecated **Netlify Large Media** service. If you are currently using it, you will need to migrate your assets to one of the supported providers mentioned above.
Also, Sveltia CMS does not support the undocumented custom media storage provider API. The `CMS.registerMediaLibrary` method is a noop in Sveltia CMS. We may add support for custom storage providers in future releases.
:::
::: info Future Plans
More integrations, including Amazon S3 and Cloudflare R2, will be added in the future.
:::
## Configuration
Relevant configuration options can be set in the `media_folder`, `public_folder`, and `media_libraries` options of your CMS configuration file. The `media_library` option from Netlify/Decap CMS is also supported for backward compatibility.
The following example demonstrates how to configure multiple providers in Sveltia CMS:
::: code-group
```yaml [YAML]
# Default media storage paths
media_folder: /static/media
public_folder: /media
# Media provider features
media_libraries:
default:
config:
max_file_size: 1024000 # default: Infinity
slugify_filename: true # default: false
transformations: # See the documentation for details
cloudinary:
config:
cloud_name: YOUR_CLOUD_NAME
api_key: YOUR_API_KEY
output_filename_only: true
uploadcare:
config:
publicKey: YOUR_PUBLIC_KEY
settings:
autoFilename: true
defaultOperations: '/resize/800x600/'
```
```toml [TOML]
# Default media storage paths
media_folder = "/static/media"
public_folder = "/media"
# Media provider features
[media_libraries.default]
[media_libraries.default.config]
max_file_size = 1024000 # default: Infinity
slugify_filename = true # default: false
# transformations: See the documentation for details
[media_libraries.cloudinary]
[media_libraries.cloudinary.config]
cloud_name = "YOUR_CLOUD_NAME"
api_key = "YOUR_API_KEY"
output_filename_only = true
[media_libraries.uploadcare]
[media_libraries.uploadcare.config]
publicKey = "YOUR_PUBLIC_KEY"
[media_libraries.uploadcare.settings]
autoFilename = true
defaultOperations = "/resize/800x600/"
```
```json [JSON]
{
"media_folder": "/static/media",
"public_folder": "/media",
"media_libraries": {
"default": {
"config": {
"max_file_size": 1024000,
"slugify_filename": true
}
},
"cloudinary": {
"config": {
"cloud_name": "YOUR_CLOUD_NAME",
"api_key": "YOUR_API_KEY"
},
"output_filename_only": true
},
"uploadcare": {
"config": {
"publicKey": "YOUR_PUBLIC_KEY"
},
"settings": {
"autoFilename": true,
"defaultOperations": "/resize/800x600/"
}
}
}
}
```
```js [JavaScript]
{
media_folder: "/static/media",
public_folder: "/media",
media_libraries: {
default: {
config: {
max_file_size: 1024000,
slugify_filename: true,
},
},
cloudinary: {
config: {
cloud_name: "YOUR_CLOUD_NAME",
api_key: "YOUR_API_KEY",
},
output_filename_only: true,
},
uploadcare: {
config: {
publicKey: "YOUR_PUBLIC_KEY",
},
settings: {
autoFilename: true,
defaultOperations: "/resize/800x600/",
},
},
},
}
```
:::
See the individual media storage provider documentation for specific configuration options and details.
::: details Legacy `media_library` Option
Sveltia CMS supports the legacy `media_library` option for backward compatibility with Netlify/Decap CMS, but it is recommended to use the `media_libraries` option for new configurations. With the legacy option, only a single media storage provider can be configured. Here is an example of configuring Cloudinary using the legacy option:
```yaml
media_library:
name: cloudinary
config:
cloud_name: YOUR_CLOUD_NAME
api_key: YOUR_API_KEY
output_filename_only: true
```
:::
---
---
url: /en/docs/frameworks/middleman.md
description: >-
Learn how to integrate Sveltia CMS with Middleman, including real-world
examples.
---
# Middleman Integration Guide
This guide provides resources and information for integrating Sveltia CMS with [Middleman](https://middlemanapp.com/), a static site generator using Ruby.
## Examples
See real-world examples of Middleman integrations in our [Showcase](/en/showcase?framework=middleman). Most of the listed sites include links to their source code, so you can explore how they implemented Sveltia CMS with Middleman.
## Development Guide
We’ll be adding a detailed development guide for integrating Sveltia CMS with Middleman in the near future. In the meantime, you can refer to the [Decap CMS documentation](https://decapcms.org/docs/middleman/), as the basic concepts are similar.
---
---
url: /en/docs/migration/earlier-versions.md
description: >-
Upgrade Sveltia CMS with guidance on deprecated options and replacement
configurations.
---
# Migrating from Earlier Versions of Sveltia CMS
This page documents the key changes and deprecations when upgrading from earlier versions of Sveltia CMS.
## Version 0.x to 1.0
We’ll update this section with specific migration steps when we release version 1.0. For now, please refer to the deprecations listed below.
## Deprecations
These options were added to Sveltia CMS 0.x but are now deprecated and will be removed in version 1.0:
* The `automatic_deployments` backend option: Use the new [`skip_ci` option](/en/docs/deployments#disabling-automatic-deployments) instead, which is more intuitive. `automatic_deployments: false` is equivalent to `skip_ci: true`, and `automatic_deployments: true` is equivalent to `skip_ci: false`.
* The `save_all_locales` i18n option: Use the [`initial_locales` option](/en/docs/i18n#disabling-non-default-locale-content) instead, which provides more flexibility. `save_all_locales: false` is equivalent to `initial_locales: all`.
* The `omit_default_locale_from_filename` i18n option: Use the new `omit_default_locale_from_file_path` i18n option instead, which applies to all multiple files/folders structures, not just `multiple_files`.
* The `multiple_folders_i18n_root` i18n structure: Use the new `multiple_root_folders` i18n structure instead, which has a more intuitive name and the same file structure.
* The `slug_length` collection option: Use the `maxlength` option in the [global slug options](/en/docs/collections/entries#global-slug-options) instead.
* The `yaml_quote` collection option: `yaml_quote: true` is equivalent to `quote: double` in the [new YAML format options](/en/docs/data-output#controlling-data-output).
* The `read_only` [UUID field](/en/docs/fields/uuid) option: Use the [`readonly` common field option](/en/docs/fields#readonly) instead (which defaults to `true` for UUID fields).
The deprecated `logo_url` option will be removed in the future. Use the [new `logo.src` option](/en/docs/customization#custom-logo) instead.
---
---
url: /en/docs/migration/netlify-decap-cms.md
description: >-
Migrate from Netlify/Decap CMS to Sveltia CMS with step-by-step instructions
and compatibility guidance.
---
# Migrating from Netlify CMS or Decap CMS
Sveltia CMS is designed as a modern [successor to Netlify CMS](/en/docs/successor-to-netlify-cms) (now Decap CMS). If you are currently using Netlify/Decap CMS, you can migrate to Sveltia CMS to take advantage of its hundreds of improvements across the board, including better performance, a more intuitive user interface, enhanced asset management, improved i18n support, and more.
::: warning Stable Version Not Yet Available
Sveltia CMS is still in beta. Although it’s already being used by various organizations and individuals in production, there might still be breaking changes before the stable 1.0 release. We recommend keeping an eye on the [release information](/en/docs/releases#release-information) for any updates.
:::
::: warning Limited Feature Parity
Some features from Netlify/Decap CMS, including **Editorial Workflow** and **Git Gateway**, are not yet implemented or will not be implemented in Sveltia CMS. Please check the compatibility section below to see if your use case is supported.
:::
## Compatibility
We are working to make Sveltia CMS compatible with Netlify/Decap CMS wherever possible so that more users can seamlessly switch to our modern successor. In some casual use cases, Sveltia CMS can be used as a drop-in replacement for Netlify/Decap CMS with just a one-line code update.
However, 100% feature parity is never planned, and some features are still missing or will not be added due to deprecation and other factors. Look at the compatibility info below to see if you can migrate now or in the near future.
### Current Limitations
We are working hard to implement several missing features from Netlify/Decap CMS. Check our [release notes](https://github.com/sveltia/sveltia-cms/releases) and [Bluesky](https://bsky.app/profile/sveltiacms.app) for updates.
The following features are not yet implemented and will be added before the 1.0 release:
* Preview for [custom editor components](/en/docs/api/editor-components) (`CMS.registerEditorComponent`)
* [Custom field types](/en/docs/api/field-types) (`CMS.registerWidget`)
* [Custom preview templates](/en/docs/api/preview-templates) (`CMS.registerPreviewTemplate`) ([#51](https://github.com/sveltia/sveltia-cms/issues/51))
* Comprehensive CMS config validation
* [Localization](/en/docs/ui#localization) of the admin UI
Due to the complexity, we have decided to **defer the following features to the 1.x or 2.0 release** expected by mid-2026. Netlify/Decap CMS has dozens of open issues with these collaboration and beta features — we want to implement them the right way.
* [Editorial workflow](/en/docs/workflows/editorial)
* [Open authoring](/en/docs/workflows/open)
* [Nested collections](/en/docs/collections/entries#creating-editable-nested-structures) (beta)
### Features Not To Be Implemented
The following features will not be implemented in Sveltia CMS due to deprecation and other factors. If you rely on any of these features, you may need to find a workaround or wait until we develop an alternative solution.
#### Deprecated Features
Other than the recently deprecated [`logo_url` option](/en/docs/customization#custom-logo), we will not support any deprecated features in Netlify/Decap CMS:
* **Git Gateway backend**: Git Gateway has been [deprecated](https://docs.netlify.com/manage/security/secure-access-to-sites/git-gateway/) by Netlify. Due to its performance limitations, we don’t plan to support it anyway. However, we plan to develop a GraphQL-based high-performance alternative [in the future](/en/docs/roadmap#v3-0) to provide a migration path for existing Git Gateway users.
* **Netlify Identity Widget**: It’s not useful without Git Gateway, and the Netlify Identity service itself has been [deprecated](https://www.netlify.com/changelog/deprecation-netlify-identity/). We plan to develop an alternative solution with role support [in the future](/en/docs/roadmap#v3-0).
* The deprecated client-side implicit grant for the GitLab backend: It has already been [removed from GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/344609). Use the [client-side PKCE authorization](/en/docs/backends/gitlab#pkce-authorization) instead.
* The deprecated Netlify Large Media service: Consider other [media storage providers](/en/docs/media).
* Deprecated camel case configuration options: Use snake case instead, according to the current Decap CMS document.
* [Entry Collection](/en/docs/collections/entries): `sortableFields`
* [DateTime](/en/docs/fields/datetime) field: `dateFormat`, `timeFormat`, `pickerUtc`
* [Markdown](/en/docs/fields/markdown) field: `editorComponents`
* [Number](/en/docs/fields/number) field: `valueType`
* [Relation](/en/docs/fields/relation) field: `displayFields`, `searchFields`, `valueField`
* Note: Some other camel case options, including Color field options, are not deprecated and will continue to work.
* The deprecated Date widget: It was removed from Decap CMS 3.0 and Sveltia CMS 0.10. Use the DateTime field type with the [`time_format: false` option](/en/docs/fields/datetime#date-only) instead.
* The deprecated [Uploadcare jQuery File Uploader](https://uploadcare.com/docs/uploads/file-uploader/): Sveltia CMS uses the API for [Uploadcare integration](/en/docs/media/uploadcare) to solve some issues. Users are prompted to enter their secret key to use the integration. This means the features found in the pre-built widget are currently unavailable. We plan to support some third-party upload sources, camera access and image editing in the future.
#### Other Features
The following features will not be implemented in Sveltia CMS due to various reasons:
* **Azure DevOps and Bitbucket backends**: For performance reasons. We’ll support these platforms if their APIs improve to allow the CMS to fetch multiple entries at once. Consider migrating to GitHub, GitLab, Gitea or Forgejo if you’d like to use Sveltia CMS now.
* [Gatsby plugin](https://github.com/decaporg/gatsby-plugin-decap-cms): In light of Gatsby’s [uncertainty](https://github.com/gatsbyjs/gatsby/discussions/39062), we won’t be investing time in developing a plugin for it. Gatsby users can still create `index.html` themselves. Note: We don’t support Netlify Identity Widget; the favicon can be specified with the `logo.src` option.
* Performance-related options: Sveltia CMS has [drastically improved performance](/en/docs/successor-to-netlify-cms#better-performance) with GraphQL enabled by default, so these are no longer relevant:
* Global: [`search`](https://decapcms.org/docs/configuration-options/#search)
* Backend: [`use_graphql`](https://decapcms.org/reference/config/backends/github/#graphql-api)
* Relation field: `options_length`
* An absolute URL in the [`public_folder`](https://decapcms.org/docs/configuration-options/#public-folder) option: Such configuration is not recommended, as stated in the Netlify/Decap CMS document.
* The theme and keymap inline settings for the Code field, along with support for some languages. Instead of [CodeMirror](https://codemirror.net/), we use Lexical’s code block functionality powered by [Prism](https://prismjs.com/), which is slated to be [replaced by Shiki](https://github.com/facebook/lexical/issues/6575).
* The `allow_multiple` option for the File and Image fields: It’s a confusing option that defaults to `true`, and there is a separate option called `media_library.config.multiple`. We have added the new [`multiple`](/en/docs/fields/file#multiple) option instead, which is more intuitive and works with all media storage providers.
* Remark plugins for the Markdown field: Not compatible with our Lexical-based rich text editor.
* The `use_secure_url` option for the [Cloudinary media storage](/en/docs/media/cloudinary): Insecure URLs should never be used.
* Local proxy server: Our [local repository workflow](/en/docs/workflows/local) eliminates the need for a proxy server. For security and performance reasons, we don’t support `netlify-cms-proxy-server` or `decap-server`. The `local_backend` option is ignored.
* The global [`locale`](https://decapcms.org/docs/configuration-options/#locale) option and `CMS.registerLocale()` method: Sveltia CMS automatically detects the user’s preferred language and changes the UI locale.
* [Undocumented methods](https://github.com/sveltia/sveltia-cms/blob/c69446da7bb0bab7405be741c0f92850c5dddfa8/src/main.js#L14-L37) exposed on the `CMS` object: This includes custom backends and custom media storage providers, if any. We may support these features in the future, but our implementation would likely be incompatible with Netlify/Decap CMS.
* Any other undocumented features and options. Exceptions apply.
### Other Breaking Changes
There are some differences in behavior between Sveltia CMS and Netlify/Decap CMS that may affect your existing configuration or content.
* [Decap CMS 3.1.1](https://github.com/decaporg/decap-cms/releases/tag/decap-cms%403.1.1) replaced Moment.js with Day.js for date handling, and In Sveltia CMS followed suit. Since [Day.js tokens](https://day.js.org/docs/en/display/format) are not 100% compatible with [Moment.js tokens](https://momentjs.com/docs/#/displaying/format/), this could be a breaking change in certain cases. Check your `format`, `date_format` and `time_format` options for DateTime fields, as well as any date formatting in [string transformations](/en/docs/string-transformations#date).
* By default, Sveltia CMS does not slugify uploaded filenames, as mentioned in the [asset management](/en/docs/successor-to-netlify-cms#better-asset-management) section. If your site generator expects hyphenated filenames, you can enable the `slugify_filename` [internal media storage option](/en/docs/media/internal#slugification-of-filenames).
* In some cases, the [data output](/en/docs/data-output) of Sveltia CMS may differ from that of Netlify/Decap CMS. Notably, Sveltia CMS does not omit empty optional fields by default. If you have data validation in your site generator, this could cause issues. Use the `omit_empty_optional_fields` [output option](/en/docs/data-output#controlling-data-output) if needed.
* Sveltia CMS requires a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts), meaning it only works with HTTPS, `localhost` or `127.0.0.1` URLs. If you’re running your own remote server and serving content over HTTP, the CMS will not work. We recommend obtaining a TLS certificate from [Let’s Encrypt](https://letsencrypt.org/).
* In Sveltia CMS, the `sanitize_preview` option for the [Markdown](/en/docs/fields/markdown) field type is set to `true` by default to prevent potential XSS attacks via entry previews. We recommend keeping this option enabled unless disabling it fixes a broken preview and you fully trust all users of your CMS.
* In Sveltia CMS, the `create` option for [entry collections](/en/docs/collections/entries) defaults to `true` because, in 99.99% of cases, users want to create new entries and adding `create: true` to every collection is redundant. To disable entry creation, set `create: false` explicitly.
* We provide only one npm package, `@sveltia/cms`, which includes all necessary code, while Netlify/Decap CMS provides [many packages](https://github.com/decaporg/decap-cms/tree/main/packages). This means `import` statement migration is not always straightforward. See the [migration steps](#migration-steps) below for details.
There may be other minor differences in behavior that are not listed here.
Sveltia CMS is also adding various config validation checks to help users identify potential issues, so you may see errors that were not present in Netlify/Decap CMS before. For example, Sveltia CMS raises an error if the `slug` collection option contains slashes (`/`), which is supposed to be invalid.
[Let us know](https://github.com/sveltia/sveltia-cms/issues/new?type=bug) if you have encounter any compatibility issues not mentioned above. We want to make the migration process as smooth as possible for our users.
## Migration Steps
### Preparation
Check the [compatibility info](/en/docs/migration/netlify-decap-cms#compatibility) above to see if your site can be migrated now or in the near future. If there are no blockers, let’s move on to the migration steps.
#### Updating Configuration
Make necessary changes if needed, such as updating your configuration file.
#### Dealing with Unsupported Features
If you’re using any features listed in the [current limitations](#current-limitations) section, you’ll need to wait until they are implemented in Sveltia CMS. We’re working hard to add these features in the coming months.
If you’re using any [features that are not going to be implemented](#features-not-to-be-implemented), you’ll need to find a workaround. For example, if you’re on Azure DevOps or Bitbucket, consider migrating to GitHub, GitLab, Gitea or Forgejo. See the next section if you’re a Git Gateway user.
#### Migrating from Git Gateway Backend
Sveltia CMS does not support the deprecated Git Gateway backend. If you don’t care about user management with Netlify Identity, you can use the [GitHub](/en/docs/backends/github) or [GitLab](/en/docs/backends/gitlab) backend instead.
To allow other people to edit content, simply invite them to your GitHub repository with the write role assigned. Please note, however, that Sveltia CMS hasn’t implemented any mechanisms to prevent conflicts in multi-user scenarios.
Once you have migrated from the Git Gateway and Netlify Identity combo, you can remove the Netlify Identity Widget script tag from your HTML:
```diff
-
```
If you want to stay with Git Gateway and Netlify Identity, unfortunately you can’t migrate to Sveltia CMS right now. We plan to develop an alternative solution [in the future](/en/docs/roadmap#v3-0).
### Switching to Sveltia CMS
Now, it’s time to switch to Sveltia CMS. Depending on how you included Netlify/Decap CMS in your project, follow the appropriate instructions below.
#### Using CDN
Replace the script tag that includes Netlify/Decap CMS with the following Sveltia CMS script tag:
```html
```
From Netlify CMS:
```diff
-
+
```
From Decap CMS:
```diff
-
+
```
Next, let’s [test Sveltia CMS on your local machine](/en/docs/workflows/local). If everything looks good, push the change to your repository.
You can now open `https://[hostname]/admin/` as usual to start editing. There is even no authentication process if you’re already signed in with a backend on Netlify/Decap CMS because Sveltia CMS uses your auth token stored in the browser. Simple enough!
#### Using Package Manager
Install Sveltia CMS:
::: code-group
```bash [npm]
npm install @sveltia/cms
```
```bash [yarn]
yarn add @sveltia/cms
```
```bash [pnpm]
pnpm add @sveltia/cms
```
```bash [bun]
bun add @sveltia/cms
```
:::
Then, update your import statements accordingly.
From Netlify CMS:
```diff
-import CMS from 'netlify-cms-app'; // or 'netlify-cms'
+import CMS from '@sveltia/cms';
```
From Decap CMS:
```diff
-import CMS from 'decap-cms-app'; // or 'decap-cms'
+import CMS from '@sveltia/cms';
```
That’s it! You have successfully migrated to Sveltia CMS. Enjoy the improved performance and features.
### Cleaning Up
You can uninstall the old Netlify/Decap CMS packages from your project to keep it clean. The packages vary depending on your setup, so uninstall all relevant ones.
A few notable changes to be aware of:
* The `netlify-cms-locales`/`decap-cms-locales` package and the `CMS.registerLocale` method are no longer needed, as Sveltia CMS automatically detects the user’s preferred language and changes the UI locale accordingly.
* If you were using `netlify-cms-proxy-server`/`decap-server`, you can stop using it and remove it from your setup. Sveltia CMS’s [local workflow](/en/docs/workflows/local) eliminates the need for a proxy server for improved security, performance and productivity. The `local_backend` option in your configuration file is no longer needed and can be removed. If you had configured a custom port number with the `.env` file, you can remove it as well.
* Sveltia CMS only publishes a single package called `@sveltia/cms`, which includes all necessary code, while Netlify/Decap CMS provides [many packages](https://github.com/decaporg/decap-cms/tree/main/packages). If you were using any other Netlify/Decap CMS packages, you may need to find alternatives or implement the functionality yourself.
### JSON Schema Setup
For a better DX, we recommend [setting up the JSON schema](/en/docs/config-basics#validation-and-autocomplete) for the CMS configuration file in your code editor. If you have the YAML extension installed, VS Code may automatically apply the outdated Netlify CMS config schema to `config.yml`. To use the latest Sveltia CMS config schema instead, you need to specify its URL.
### AI Tools Support
This documentation site provides `llms.txt` files that you can use with AI tools like GitHub Copilot, Claude and ChatGPT to help them understand Sveltia CMS better. See [AI Tools Support](/en/docs/config-basics#ai-tools-support) for details.
### Authentication
No changes are needed for authentication if you are using the GitHub, GitLab or Gitea/Forgejo backend. Sveltia CMS will use the existing auth tokens stored in the browser.
If you have set up an OAuth application for Netlify/Decap CMS, you can continue using it with Sveltia CMS. There is no need to create a new OAuth app.
::: tip Note for Netlify Customers
If you currently use Netlify to sign in with GitHub or GitLab and stay on Netlify, no changes are needed. Sveltia CMS works seamlessly with Netlify’s authentication system. However, if you’re moving to a different hosting service, you will need to use a different authentication method. See the [GitHub backend](/en/docs/backends/github#authentication) or [GitLab backend](/en/docs/backends/gitlab#authentication) documentation for more details.
:::
### Content Security Policy (CSP)
Unlike Netlify/Decap CMS, Sveltia CMS does not require the `unsafe-eval` and `unsafe-inline` keywords in the `script-src` CSP directive. However, new CSP rules may be needed depending on your configuration, such as the media storage providers you use. See [setting up Content Security Policy](/en/docs/security#setting-up-content-security-policy) for more information.
## Other Notable Differences
Some differences between Sveltia CMS and Netlify/Decap CMS may affect your existing configuration or content. Here are some notable ones to be aware of:
### Terminology
Some features have different names in Sveltia CMS compared to Netlify/Decap CMS. These differences are mostly cosmetic, and the underlying concepts remain the same. There are no changes in functionality.
| Netlify/Decap CMS | Sveltia CMS |
| --- | --- |
| [Media library](https://decapcms.org/docs/configuration-options/#media-library) | [Media storage provider](/en/docs/media) |
| [Folder collection](https://decapcms.org/docs/collection-folder/) | [Entry collection](/en/docs/collections/entries) |
| [Widget](https://decapcms.org/docs/widgets/) | [Field type](/en/docs/fields) |
| [Summary string transformation](https://decapcms.org/docs/summary-strings/) | [String transformation](/en/docs/string-transformations) |
### Content Editing Experience
Sveltia CMS marks required fields for efficient data entry. This is the opposite of Netlify/Decap CMS, which marks optional fields. This change aims to reduce visual clutter and help users focus on the essential fields that must be filled out.
When [i18n support](/en/docs/i18n) is enabled, Sveltia CMS requires all locales to have values for required fields. In contrast, Netlify/Decap CMS only enforces this for the default locale. This change ensures that content is complete across all locales. If you rely on the previous behavior, you can set the `required` [field-level configuration](/en/docs/i18n#field-level-configuration) to include only specific locales.
### Data Output
The data output conventions of Sveltia CMS may differ from that of Netlify/Decap CMS in some cases. See the [data output](/en/docs/data-output#data-output-conventions) documentation for details.
You don’t need to manually update your existing content — the CMS automatically handles these differences when loading existing content. However, there are two notable differences to be aware of:
* Sveltia CMS does not omit empty optional fields by default. If you have data validation in your framework, this could cause issues. Use the `omit_empty_optional_fields` [output option](/en/docs/data-output#controlling-data-output) if needed.
* Markdown uses soft line breaks (single line breaks) instead of hard line breaks (escaped line breaks `\`). In your framework, you may need to [enable the appropriate option](/en/docs/how-tos#rendering-soft-line-breaks-as-hard-line-breaks-in-markdown) to render soft line breaks as hard line breaks.
### Preview Styles
Sveltia CMS comes with a minimum default preview style to ensure better readability. If you have [custom preview styles](/en/docs/api/preview-styles) for Netlify/Decap CMS, you could remove them or adapt them to Sveltia CMS, which shows field labels in the preview by default.
---
---
url: /en/docs/migration/static-cms.md
description: >-
Migrate from Static CMS to Sveltia CMS with compatibility guidance and
configuration steps.
---
# Migrating from Static CMS
Sveltia CMS provides partial compatibility with [Static CMS](https://github.com/StaticJsCMS/static-cms), an archived fork of Netlify CMS. Since Static CMS was archived over a year ago, we don’t plan to implement additional compatibility beyond what’s listed below. However, we may still adopt some of their features that we find useful.
## Compatibility
Static CMS made [some breaking changes](https://staticjscms.netlify.app/docs/decap-migration-guide) while Sveltia CMS mostly follows Netlify/Decap CMS, so you should review your configuration carefully.
### Configuration Options
* Sveltia CMS supports the [`sortable_fields`](/en/docs/collections/entries#sorting), [`view_filters`](/en/docs/collections/entries#filtering) and [`view_groups`](/en/docs/collections/entries#grouping) options with the new `default` option. We still support the legacy Netlify/Decap CMS format as well, so you can use either format for these options.
* Directory navigation in the Asset Library is partially supported in Sveltia CMS. If you define [collection-specific `media_folder`s](/en/docs/media/internal#collection-level-configuration), these folders will be displayed in the Asset Library and Select File/Image dialog. We plan to implement the display of subfolders within a configured folder in Sveltia CMS 2.0. We don’t plan to support the `folder_support` and `display_in_navigation` options for `media_library`; subfolders will be displayed with no configuration. ([#301](https://github.com/sveltia/sveltia-cms/issues/301))
* The `logo_link` global option will not be supported. Use `display_url` or `site_url` instead.
* The `yaml` global option will not be supported, as Sveltia CMS does not expose underlying `yaml` library options for forward compatibility reasons. However, we do have some [data output options](/en/docs/data-output#controlling-data-output), including YAML indentation and quotes.
### I18n Support
* The `enforce_required_non_default` i18n option will not be supported. Sveltia CMS enforces required fields in all locales by default. However, the `initial_locales` i18n option allows users to [disable non-default locales](/en/docs/i18n#disabling-non-default-locale-content) if needed. Developers can also specify a subset of locales with the `required` field option, e.g. `required: [en]`.
### Widgets
* The date/time format options for the DateTime widget are **not compatible** since Static CMS [switched to date-fns](https://staticjscms.netlify.app/docs/decap-migration-guide#dates) while Decap CMS and Sveltia CMS have replaced Moment.js with Day.js. Update your formats accordingly.
* The [KeyValue widget](/en/docs/fields/keyvalue) is implemented in Sveltia CMS with the same options.
* The [UUID widget](/en/docs/fields/uuid) is also implemented, but with different options.
* The `prefix` and `suffix` options for the Boolean, Number and String widgets are implemented as `before_input` and `after_input` in Sveltia CMS, respectively. Our `prefix` and `suffix` options for the String widget are literally a prefix and suffix to the value.
* The `multiple` option for the File and Image widgets is supported in Sveltia CMS, along with the `min` and `max` options.
* The [breaking change to the List widget](https://staticjscms.netlify.app/docs/decap-migration-guide#list-widget) doesn’t apply to Sveltia CMS. You must use the `field` (singular) option to produce a single subfield with [no `name` output](/en/docs/data-output#understanding-exceptions).
### Customization
* `CMS.registerIcon()` will not be supported, as Sveltia CMS includes the Material Symbols font for [custom collection icons](/en/docs/collections#icons) that doesn’t require manual registration.
---
---
url: /en/docs/migration.md
description: >-
Migrate to Sveltia CMS from Netlify/Decap CMS and other platforms with
step-by-step guides.
---
# Migration Guides
This document provides guidance on migrating from other CMS platforms to Sveltia CMS. Specific instructions are provided for Netlify/Decap CMS.
## Migrating from Other CMSs
Migrating from other CMS platforms to Sveltia CMS can vary in complexity depending on the source CMS. Below are guides for some of the more common platforms.
### Netlify CMS or Decap CMS
Sveltia CMS is designed to be a direct [successor to Netlify CMS](/en/docs/successor-to-netlify-cms) (now Decap CMS). A migration from Netlify/Decap CMS to Sveltia CMS is intended to be as seamless as possible. This guide will walk you through the necessary steps to migrate your existing setup to Sveltia CMS.
* [Migrating from Netlify CMS or Decap CMS](/en/docs/migration/netlify-decap-cms)
### Static CMS
Static CMS was a community fork of Netlify CMS that introduced several unique features. While we don’t have a full migration guide for Static CMS, many of the concepts and configurations are similar to those in Netlify CMS. We have documented some of the key differences and considerations when migrating from Static CMS to Sveltia CMS.
* [Migrating from Static CMS](/en/docs/migration/static-cms)
### Pages CMS
We’re planning to create a migration path for [Pages CMS](https://pagescms.org/), which was also influenced by Netlify CMS but appears to be in maintenance mode. The core concepts like a YAML configuration file and Git-based content storage are similar to Sveltia CMS. Stay tuned for updates.
### Other Headless CMS Platforms
We will continue to expand our migration resources to cover popular headless CMS platforms. If you are using a different CMS and are interested in migrating to Sveltia CMS, check back here for future migration guides or just look at the [Start Guide](/en/docs/start) to get started.
If your current CMS is Git-based, the migration process should be relatively straightforward. Typically, you will need to configure your Sveltia CMS setup to match your existing content structure.
### Traditional CMS Platforms
We have observed that users of traditional platforms, such as WordPress and Wix, are also migrating to static sites with Sveltia CMS. This approach is often preferred by small site owners and developers because of its cost-effectiveness, performance, and flexibility.
The migration process involves exporting content as static files, typically in Markdown format, and creating a new site from scratch using your preferred framework. Our [Start Guide](/en/docs/start) can help you get started with Sveltia CMS.
## Migrating from Earlier Versions of Sveltia CMS
See the detailed migration instructions here:
* [Migrating from Earlier Versions of Sveltia CMS](/en/docs/migration/earlier-versions)
---
---
url: /en/docs/frameworks/next.md
description: >-
Learn how to integrate Sveltia CMS with Next.js, including real-world
examples.
---
# Next.js Integration Guide
This guide provides resources and information for integrating Sveltia CMS with [Next.js](https://nextjs.org/), a popular React framework for building server-side rendered and static websites.
## Examples
See real-world examples of Next.js integrations in our [Showcase](/en/showcase?framework=next). Most of the listed sites include links to their source code, so you can explore how they implemented Sveltia CMS with Next.js.
## Development Guide
We’ll be adding a detailed development guide for integrating Sveltia CMS with Next.js in the near future. In the meantime, you can refer to the [Decap CMS documentation](https://decapcms.org/docs/nextjs/), as the basic concepts are similar.
---
---
url: /en/docs/fields/number.md
description: Input numeric values in Sveltia CMS with increment and decrement support.
---
# Number Field
The Number field type allows users to input numeric values using a specialized input field that supports incrementing and decrementing values.
## User Interface
### Editor
Text input field that only accepts numeric values. It includes up and down arrows for incrementing or decrementing the value, as well as support for decimal points and negative numbers.
Additional text can be displayed before or after the input field using the `before_input` and `after_input` options.
### Preview
A localized string representation of the number, formatted according to the preview locale.
## Data Type
A number if the `value_type` option is set to `int` (default) or `float`. If the `required` option is set to `false` and the field is left empty, the value will be `null`.
If `value_type` is other than `int` or `float`, the value will be stored as a string.
## Data Validation
* If the `required` option is set to `true`, the number value must not be `null` (i.e., the field must not be left empty).
* The number value must be within the range defined by the `min` and `max` options, if specified.
* The number value must conform to the type defined by the `value_type` option.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the Number field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `number`.
### Optional Options
::: warning Breaking change from Netlify/Decap CMS
Sveltia CMS does not support the deprecated camelCase `valueType` option. Use `value_type` instead.
:::
#### `default`
* **Type**: `number`
* **Default**: `null`
The default value for the field.
#### `value_type`
* **Type**: `string`
* **Default**: `int`
The type of value to store. Can be one of the following:
* `int`: The UI only accepts integer input, and the value is stored as an integer.
* `float`: The UI accepts decimal input, and the value is stored as a floating-point number.
* `int/string`: The UI only accepts integer input, but the value is stored as a string.
* `float/string`: The UI accepts decimal input, but the value is stored as a string.
::: tip Note for Netlify/Decap CMS users
The [Netlify/Decap CMS document](https://decapcms.org/docs/widgets/#Number) says the `value_type` option accepts any type other than `int` and `float` , which results in the value being stored as a string. However, it actually doesn’t work in Decap CMS. So, Sveltia CMS only supports `int` and `float`, along with the new `int/string` and `float/string` types. Other types will default to `int`.
:::
#### `min`
* **Type**: `number`
* **Default**: `-Infinity`
The minimum allowed value for the field. This enables validation to ensure that users enter a value greater than or equal to this minimum.
#### `max`
* **Type**: `number`
* **Default**: `Infinity`
The maximum allowed value for the field. This enables validation to ensure that users enter a value less than or equal to this maximum.
#### `step`
* **Type**: `number`
* **Default**: `1`
The increment/decrement step for the input field. This determines the amount by which the value changes when using the up and down arrows.
#### `before_input`
* **Type**: `string`
* **Default**: `""`
Additional text to display before the input field.
#### `after_input`
* **Type**: `string`
* **Default**: `""`
Additional text to display after the input field.
## Examples
### Basic Number Field
This example demonstrates a simple Number field configuration that allows users to input integer values.
::: code-group
```yaml [YAML]
- name: quantity
label: Quantity
widget: number
```
```toml [TOML]
[[fields]]
name = "quantity"
label = "Quantity"
widget = "number"
```
```json [JSON]
{
"name": "quantity",
"label": "Quantity",
"widget": "number"
}
```
```js [JavaScript]
{
name: 'quantity',
label: 'Quantity',
widget: 'number',
}
```
:::
Output example:
::: code-group
```yaml [YAML]
quantity: 5
```
```toml [TOML]
quantity = 5
```
```json [JSON]
{
"quantity": 5
}
```
:::
### Price Field
This example demonstrates a Number field configured to store floating-point values, suitable for representing prices. It includes a dollar sign before the input field and sets a default value.
::: code-group
```yaml [YAML]
- name: price
label: Price
widget: number
value_type: float
before_input: $
default: 9.99
```
```toml [TOML]
[[fields]]
name = "price"
label = "Price"
widget = "number"
value_type = "float"
before_input = "$"
default = 9.99
```
```json [JSON]
{
"name": "price",
"label": "Price",
"widget": "number",
"value_type": "float",
"before_input": "$",
"default": 9.99
}
```
```js [JavaScript]
{
name: 'price',
label: 'Price',
widget: 'number',
value_type: 'float',
before_input: '$',
default: 9.99,
}
```
:::
Output example:
::: code-group
```yaml [YAML]
price: 19.99
```
```toml [TOML]
price = 19.99
```
```json [JSON]
{
"price": 19.99
}
```
:::
---
---
url: /en/docs/frameworks/nuxt.md
description: 'Learn how to integrate Sveltia CMS with Nuxt, including real-world examples.'
---
# Nuxt Integration Guide
This guide provides resources and information for integrating Sveltia CMS with [Nuxt](https://nuxt.com/), a popular Vue.js framework for building server-side rendered and static websites.
## Examples
See real-world examples of Nuxt integrations in our [Showcase](/en/showcase?framework=nuxt). Most of the listed sites include links to their source code, so you can explore how they implemented Sveltia CMS with Nuxt.
## Development Guide
We’ll be adding a detailed development guide for integrating Sveltia CMS with Nuxt in the near future. In the meantime, you can refer to the [Decap CMS documentation](https://decapcms.org/docs/nuxt/), as the basic concepts are similar.
---
---
url: /en/docs/fields/object.md
description: Create and manage nested objects in Sveltia CMS within entry forms.
---
# Object Field
The Object field type allows users to create and manage nested objects within the CMS entry form. It provides a structured way to group related fields together.
## User Interface
### Editor
The Object field type has two different UI modes, depending on the configuration. You can have conditional subfields using either the `fields` option or the `types` option.
* With the `fields` option: A group of subfield editors is shown within a collapsible section. If `required` is set to `false`, a checkbox to add or remove the object is displayed.
* With the `types` option: A type selector is shown, along with the corresponding subfield editors for the selected type. This configuration is called a **variable type** object. It’s useful for creating flexible content structures like page builders.
### Preview
A read-only view of the object’s content, displaying the values of its nested fields in a structured format.
## Data Type
An object containing nested fields as defined in the configuration.
If the `required` option is set to `false` and subfields are not added, the value will be `null`.
## Data Validation
* If the `required` option is set to `true`, the object must not be `null` (i.e., a type must be selected if using variable types).
## Options
In addition to the [common field options](/en/docs/fields#common-options), the Object field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `object` to use the Object field type.
#### `fields`
* **Type**: `array` of [field definitions](/en/docs/fields)
Either `fields` or `types` must be provided. You cannot use both options simultaneously.
#### `types`
* **Type**: `array` of variable type definitions
Either `fields` or `types` must be provided. You cannot use both options simultaneously.
Each type definition is an object with the following properties:
* `name` (string, required): The unique identifier for the type.
* `label` (string, required): The display label for the type.
* `widget` (string, optional): The field type for this type. It must be `object` if not omitted. Other field types are invalid.
* `fields` (array of field definitions, optional): The subfields for this type.
### Optional Options
#### `default`
* **Type**: `object`
* **Default**: `{}`
The default value for the object field.
#### `collapsed`
* **Type**: `boolean` or `auto`
* **Default**: `false`
Whether the object field is initially collapsed in the UI. If set to `auto`, the UI is collapsed if the object has any filled subfields and expanded if all the subfields are empty.
#### `summary`
* **Type**: `string`
* **Default**: `""`
A string template used to generate a summary of the object’s content when it is collapsed in the UI. The template can include placeholders for subfield values using the syntax `{{fieldName}}`. [String transformations](/en/docs/string-transformations) can be applied in this option.
#### `typeKey`
* **Type**: `string`
* **Default**: `"type"`
The key used to store the selected type name in a variable type object. The default key is `type`.
You cannot use a key that conflicts with any of the subfield names defined in the object.
::: tip
Unlike most of other config options, `typeKey` is camelCased.
:::
## Examples
### Standard Object
The following example defines an Object field named `author` with two subfields: `name` (a string) and `bio` (a text area).
::: code-group
```yaml [YAML]
- name: author
label: Author
widget: object
fields:
- name: name
label: Name
widget: string
- name: bio
label: Biography
widget: text
```
```toml [TOML]
[[fields]]
name = "author"
label = "Author"
widget = "object"
[[fields.fields]]
name = "name"
label = "Name"
widget = "string"
[[fields.fields]]
name = "bio"
label = "Biography"
widget = "text"
```
```json [JSON]
{
"name": "author",
"label": "Author",
"widget": "object",
"fields": [
{
"name": "name",
"label": "Name",
"widget": "string"
},
{
"name": "bio",
"label": "Biography",
"widget": "text"
}
]
}
```
```js [JavaScript]
{
name: "author",
label: "Author",
widget: "object",
fields: [
{
name: "name",
label: "Name",
widget: "string",
},
{
name: "bio",
label: "Biography",
widget: "text",
},
],
},
```
:::
Output example:
::: code-group
```yaml [YAML]
author:
name: Jane Doe
bio: Jane Doe is a writer and editor with over 10 years of experience.
```
```toml [TOML]
[author]
name = "Jane Doe"
bio = "Jane Doe is a writer and editor with over 10 years of experience."
```
```json [JSON]
{
"author": {
"name": "Jane Doe",
"bio": "Jane Doe is a writer and editor with over 10 years of experience."
}
}
```
:::
### Nested Object
An object can contain another object as a subfield. The following example defines an Object field named `book` with a nested Object field named `publisher`.
::: code-group
```yaml [YAML]
- name: book
label: Book
widget: object
fields:
- name: title
label: Title
widget: string
- name: publisher
label: Publisher
widget: object
fields:
- name: name
label: Name
widget: string
- name: address
label: Address
widget: text
```
```toml [TOML]
[[fields]]
name = "book"
label = "Book"
widget = "object"
[[fields.fields]]
name = "title"
label = "Title"
widget = "string"
[[fields.fields]]
name = "publisher"
label = "Publisher"
widget = "object"
[[fields.fields.fields]]
name = "name"
label = "Name"
widget = "string"
[[fields.fields.fields]]
name = "address"
label = "Address"
widget = "text"
```
```json [JSON]
{
"name": "book",
"label": "Book",
"widget": "object",
"fields": [
{
"name": "title",
"label": "Title",
"widget": "string"
},
{
"name": "publisher",
"label": "Publisher",
"widget": "object",
"fields": [
{
"name": "name",
"label": "Name",
"widget": "string"
},
{
"name": "address",
"label": "Address",
"widget": "text"
}
]
}
]
}
```
```js [JavaScript]
{
name: "book",
label: "Book",
widget: "object",
fields: [
{
name: "title",
label: "Title",
widget: "string",
},
{
name: "publisher",
label: "Publisher",
widget: "object",
fields: [
{
name: "name",
label: "Name",
widget: "string",
},
{
name: "address",
label: "Address",
widget: "text",
},
],
},
],
},
```
:::
Output example:
::: code-group
```yaml [YAML]
book:
title: The Great Gatsby
publisher:
name: Scribner
address: '123 Publisher St, New York, NY'
```
```toml [TOML]
[book]
title = "The Great Gatsby"
[book.publisher]
name = "Scribner"
address = "123 Publisher St, New York, NY"
```
```json [JSON]
{
"book": {
"title": "The Great Gatsby",
"publisher": {
"name": "Scribner",
"address": "123 Publisher St, New York, NY"
}
}
}
```
:::
### Variable Type
The following example defines a variable type Object field named `contentBlock` with three types: `textBlock`, `imageBlock`, and `placeholderBlock`. Note that the `placeholderBlock` type does not have any subfields but is still a valid type.
::: code-group
```yaml [YAML]
- name: contentBlock
label: Content Block
widget: object
types:
- name: textBlock
label: Text Block
fields:
- name: text
label: Text
widget: text
- name: imageBlock
label: Image Block
fields:
- name: image
label: Image
widget: image
- name: placeholderBlock
label: Placeholder Block
```
```toml [TOML]
[[fields]]
name = "contentBlock"
label = "Content Block"
widget = "object"
[[fields.types]]
name = "textBlock"
label = "Text Block"
[[fields.types.fields]]
name = "text"
label = "Text"
widget = "text"
[[fields.types]]
name = "imageBlock"
label = "Image Block"
[[fields.types.fields]]
name = "image"
label = "Image"
widget = "image"
[[fields.types]]
name = "placeholderBlock"
label = "Placeholder Block"
```
```json [JSON]
{
"name": "contentBlock",
"label": "Content Block",
"widget": "object",
"types": [
{
"name": "textBlock",
"label": "Text Block",
"fields": [
{
"name": "text",
"label": "Text",
"widget": "text"
}
]
},
{
"name": "imageBlock",
"label": "Image Block",
"fields": [
{
"name": "image",
"label": "Image",
"widget": "image"
}
]
}
{
"name": "placeholderBlock",
"label": "Placeholder Block"
}
]
}
```
```js [JavaScript]
{
name: "contentBlock",
label: "Content Block",
widget: "object",
types: [
{
name: "textBlock",
label: "Text Block",
fields: [
{
name: "text",
label: "Text",
widget: "text",
},
],
},
{
name: "imageBlock",
label: "Image Block",
fields: [
{
name: "image",
label: "Image",
widget: "image",
},
],
},
{
name: "placeholderBlock",
label: "Placeholder Block",
},
],
},
```
:::
The output will vary based on the selected type, which is indicated by the `type` key (customizable via the `typeKey` option). If no `fields` are defined for a type, the object will only contain the `type` key, as shown in the `placeholderBlock` example below.
Output example for a `textBlock` type:
::: code-group
```yaml [YAML]
contentBlock:
type: textBlock
text: 'This is a sample text block.'
```
```toml [TOML]
[contentBlock]
type = "textBlock"
text = "This is a sample text block."
```
```json [JSON]
{
"contentBlock": {
"type": "textBlock",
"text": "This is a sample text block."
}
}
```
:::
Output example for an `imageBlock` type:
::: code-group
```yaml [YAML]
contentBlock:
type: imageBlock
image: /images/sample.jpg
```
```toml [TOML]
[contentBlock]
type = "imageBlock"
image = "/images/sample.jpg"
```
```json [JSON]
{
"contentBlock": {
"type": "imageBlock",
"image": "/images/sample.jpg"
}
}
```
:::
Output example for a `placeholderBlock` type:
::: code-group
```yaml [YAML]
contentBlock:
type: placeholderBlock
```
```toml [TOML]
[contentBlock]
type = "placeholderBlock"
```
```json [JSON]
{
"contentBlock": {
"type": "placeholderBlock"
}
}
```
:::
---
---
url: /en/docs/workflows/open.md
description: >-
Implement an open authoring workflow in Sveltia CMS for community
contributions.
---
# Open Authoring
Open Authoring is a workflow that allows contributors to propose changes to a project without requiring direct write access to the repository. This is typically done through fork-and-pull request mechanisms, enabling a wider range of contributors to participate in content creation and editing.
::: warning Unimplemented
This feature is not yet supported in Sveltia CMS. It will be added in the near future.
:::
## Use Cases
* Open source projects that welcome contributions from the community.
* Projects that require a formal review process for external contributions.
* Situations where contributors may not have direct access to the main repository.
* Workflows that involve multiple stages of review and approval for external contributions.
## Requirements
The [GitHub](/en/docs/backends/github) backend must be used.
::: info Future Plans
Support for other Git backends may be added in the future.
:::
## Configuration
Add the `open_authoring` option to your CMS configuration’s `backend` settings:
::: code-group
```yaml{4} [YAML]
backend:
name: github
repo: user/repo
open_authoring: true
```
```toml{4} [TOML]
[backend]
name = "github"
repo = "user/repo"
open_authoring = true
```
```json{5} [JSON]
{
"backend": {
"name": "github",
"repo": "user/repo",
"open_authoring": true
}
}
```
```js{5} [JavaScript]
{
backend: {
name: 'github',
repo: 'user/repo',
open_authoring: true,
},
}
```
:::
---
---
url: /en/docs/privacy.md
description: >-
Review Sveltia CMS privacy practices, data handling policies, and GDPR
compliance.
---
# Privacy
We don’t have a privacy policy because we don’t collect any personal data. That said, here is some information about how the Sveltia CMS application handles data. This information is provided for transparency purposes only.
## Application Data Handling
Sveltia CMS is not a service but a client-side application that runs in your web browser. You don’t need an account to use the app, but you do need to authenticate with your Git hosting provider to read and write remote data. All content is stored in your Git repository. No data is sent to any server operated by us.
Depending on your CMS configuration, you will need to use an OAuth application hosted by yourself or a third party, such as Netlify or Cloudflare, to retrieve an access token from GitHub. Alternatively, you can provide an access token directly on the CMS’s sign-in page. In any case, your token is stored in your browser’s local storage, and subsequent API requests are made directly between your browser and the Git hosting provider.
The CMS also integrates with various third-party services, including stock photo providers and translation services. These are “bring your own key” (BYOK) features that are entirely optional. You provide your own API keys for these services, which are stored in your browser’s local storage, and API requests are then made directly between your browser and the relevant service providers.
Since we don’t even collect any analytics data, we don’t have a privacy policy. For third-party services, please refer to their respective privacy policies.
## Web Analytics
We use [Cloudflare Web Analytics](https://www.cloudflare.com/web-analytics/) to collect anonymous statistics about site visitors. This service does not use cookies and complies with GDPR regulations. No personal data is collected or stored.
## GDPR Compliance
---
---
url: /en/docs/fields/relation.md
description: Create relationships between entries in Sveltia CMS across collections.
---
# 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](/en/docs/collections/entries)
* List items in a specific file in a [file collection](/en/docs/collections/files)
## 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.
::: info Future Plans
Currently, it’s not possible to create new related entries directly from the Relation field UI. We plan to add this feature in future releases.
:::
### 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.
::: warning Cascading unimplemented
Cascade updates and deletions like relational databases are not yet supported. If a related entry is updated or deleted, the Relation field will not automatically reflect those changes. Users must manually update the Relation field values to maintain data integrity. We plan to add cascading support in the near future.
:::
## Data Validation
* If the `required` option is set to `true`, at least one related entry must be selected.
* If the `multiple` option is enabled, the number of selected entries must be between the `min` and `max` limits, if specified.
## Options
In addition to the [common field options](/en/docs/fields#common-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. If the target collection is a file collection, the `file` option must also be specified.
### Optional Options
::: warning 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](/en/docs/collections/files) to relate to. Required if the target collection is a file 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., `id` or `title`.
* A template string that references fields in the related collection using the syntax `{{field_name}}`. For example, `{{fields.id}}` or `{{fields.title}}`.
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](/en/docs/i18n).
When using template strings, keep the following in mind:
* A field named `slug` must be prefixed with `fields.` 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 the `field` or `fields` option.
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**: `array` of `strings`
* **Default**: `["title"]` if `value_field` is `{{slug}}`, otherwise the value of `value_field` option
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:
::: code-group
```yaml [YAML]
display_fields: ['{{first_name}} {{last_name}}']
```
```toml [TOML]
display_fields = ["{{first_name}} {{last_name}}"]
```
```json [JSON]
{
"display_fields": ["{{first_name}} {{last_name}}"]
}
```
```js [JavaScript]
{
display_fields: ["{{first_name}} {{last_name}}"],
}
```
:::
::: code-group
```yaml [YAML]
display_fields: ['first_name', 'last_name']
```
```toml [TOML]
display_fields = ["first_name", "last_name"]
```
```json [JSON]
{
"display_fields": ["first_name", "last_name"]
}
```
```js [JavaScript]
{
display_fields: ["first_name", "last_name"],
}
```
:::
#### `search_fields`
* **Type**: `array` of `strings`
* **Default**: value of `display_fields` option
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. By default, it uses the same fields as specified in the `display_fields` option.
#### `default`
* **Type**: `string`, `number`, `array of strings`, or `array of numbers`
* **Default**: `null` or `[]`
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.
#### `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**: `array` of filter objects
* **Default**: `[]`
An array of filter objects to limit the related entries shown in the Relation field UI. Each filter object should have the following properties:
* `field`: The field name in the related collection to filter on.
* `values`: An array of strings or numbers representing the values to match for the specified field
Example:
::: code-group
```yaml [YAML]
filters:
- field: draft
values: [false]
- field: category
values: ['news', 'updates']
```
```toml [TOML]
[[filters]]
field = "draft"
values = [false]
[[filters]]
field = "category"
values = ["news", "updates"]
```
```json [JSON]
{
"filters": [
{
"field": "draft",
"values": [false]
},
{
"field": "category",
"values": ["news", "updates"]
}
]
}
```
```js [JavaScript]
{
filters: [
{
field: 'draft',
values: [false],
},
{
field: 'category',
values: ['news', 'updates'],
},
],
}
```
:::
## Examples
### Selecting Entries from an Entry Collection
Assuming you have the following entry collection named `categories`:
::: code-group
```yaml [YAML]
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
```
```toml [TOML]
[[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"
```
```json [JSON]
{
"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"
}
]
}
]
}
```
```js [JavaScript]
{
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:
::: code-group
```yaml [YAML]
fields:
- name: category
label: Category
widget: relation
collection: categories
value_field: slug
display_fields: [title]
search_fields: [title, description]
```
```toml [TOML]
[[fields]]
name = "category"
label = "Category"
widget = "relation"
collection = "categories"
value_field = "slug"
display_fields = ["title"]
search_fields = ["title", "description"]
```
```json [JSON]
{
"fields": [
{
"name": "category",
"label": "Category",
"widget": "relation",
"collection": "categories",
"value_field": "slug",
"display_fields": ["title"],
"search_fields": ["title", "description"]
}
]
}
```
```js [JavaScript]
{
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`:
::: code-group
```yaml [YAML]
category: news
```
```toml [TOML]
category = "news"
```
```json [JSON]
{
"category": "news"
}
```
:::
### Referencing a File in a File Collection, Multiple Select
Assuming you have the following `cities` file in a `data` file collection:
::: code-group
```yaml [YAML]
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
```
```toml [TOML]
[[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"
```
```json [JSON]
{
"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"
}
]
}
]
}
]
}
]
}
```
```js [JavaScript]
{
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:
::: code-group
```yaml [YAML]
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}}']
```
```toml [TOML]
[[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}}"]
```
```json [JSON]
{
"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}}"]
}
]
}
```
```js [JavaScript]
{
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”:
::: code-group
```yaml [YAML]
favorite_cities:
- San Francisco
- Tokyo
- Paris
```
```toml [TOML]
favorite_cities = ["San Francisco", "Tokyo", "Paris"]
```
```json [JSON]
{
"favorite_cities": ["San Francisco", "Tokyo", "Paris"]
}
```
:::
---
---
url: /en/docs/releases.md
description: >-
Follow Sveltia CMS updates, release notes, versioning, and upgrade
instructions for your installation.
---
# Releases
This document provides information about Sveltia CMS releases, including how to find release notes, versioning practices, and update instructions.
## Release Frequency
Sveltia CMS ships new versions frequently, usually multiple times per week or even multiple times per day. Instead of following a strict release schedule, we prioritize releasing updates as soon as they are ready. This approach allows us to deliver new features, improvements, and bug fixes to users more rapidly.
This approach works because our product is a [CDN-served SPA](/en/docs/architecture#how-sveltia-cms-works). As long as you use the CDN script without pinning to a specific version, there is usually no maintenance overhead for users to update their installations. End-users can always access the latest version of Sveltia CMS without needing to manually update their software.
## Release Information
Here are some ways to stay informed about new releases:
* Our [release notes](https://github.com/sveltia/sveltia-cms/releases) are available on GitHub. You can star the repository to receive notifications about new releases.
* Major and minor releases are also announced on our [Bluesky account](https://bsky.app/profile/sveltiacms.app). Follow us there for the latest updates!
* Interested in what’s coming next? Check out our [roadmap](/en/docs/roadmap). We have a lot of exciting ideas to make Sveltia CMS the best Git-based CMS out there.
* Deprecated features and breaking changes are documented in the [migration guide](/en/docs/migration/earlier-versions).
## Versioning Policy
We follow [semantic versioning](https://semver.org/) for our releases. We don’t typically maintain multiple release lines, but we may do so for critical security fixes if necessary. We don’t offer long-term support (LTS) versions.
Breaking changes are only introduced in major version updates (e.g., from 1.x.x to 2.0.0). Minor and patch releases (e.g., from 1.0.0 to 1.1.0 or 1.0.1) are backward-compatible, though unintended issues may occasionally arise.
::: warning Stable Version Not Yet Available
At this point, Sveltia CMS has not reached 1.0.0, so breaking changes may still occur in minor version updates. We’ll announce breaking changes clearly in the release notes.
:::
## Checking Your Current Version
You can view the current version of Sveltia CMS you’re using by enabling [Developer Mode](/en/docs/ui#developer-mode) in the application:
1. Click on your avatar in the top right corner of the application to open the Account menu.
2. Click Settings.
3. Click the Advanced tab.
4. Enable Developer Mode.
5. Close the Settings dialog.
A Release Notes link will now appear under the Help menu with the `?` icon, displaying the current application version.
## Updating Your Installation
### CDN Usage
Updating Sveltia CMS when using the CDN is automatic, unless you have pinned to a specific version. The CDN always serves the latest version, so you don’t need to take any action to receive updates.
To use the latest version of Sveltia CMS via the CDN, include the following script tag in your HTML, as the [start guide](/en/docs/start) suggests:
```html
```
::: tip Module Version
Earlier versions of Sveltia CMS were bundled as a [JavaScript module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules), so you needed `type="module"` in the script. The current version is a classic [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) script, so the `type` attribute is no longer necessary. If you still need to use the module version for some reason, you can use this URL instead:
```
https://unpkg.com/@sveltia/cms/dist/sveltia-cms.mjs
```
:::
The CMS also periodically checks for updates and notifies you when a new version is available. When you see the notification, simply click the “Update Now” button to refresh the application and load the latest version.
To avoid major version updates that may include breaking changes, you can pin to a specific major version using the caret symbol (`^`) followed by the major version number:
```html
```
::: warning Stable Version Not Yet Available
At this point, Sveltia CMS has not reached 1.0.0, so breaking changes may still occur in minor version updates. Pinning to a specific major version is more relevant once we start releasing stable versions (1.0.0 and above).
:::
You can also pin to an exact version, but this is not recommended unless you have a specific reason, like debugging or temporarily avoiding a problematic release:
```html
```
### NPM Package Usage
If you’ve chosen to install the CMS using a package manager, updating the package is your responsibility. We strongly recommend using [`ncu`](https://www.npmjs.com/package/npm-check-updates) or a service like [Dependabot](https://github.blog/2020-06-01-keep-all-your-packages-up-to-date-with-dependabot/) to keep dependencies up to date. Otherwise, you’ll miss important bug fixes and new features.
::: tip ProTip
We update our dependencies using `ncu -u && pnpm up` at least once a week.
:::
The pinning methods described in the CDN section above also apply to NPM package versions. For example, to pin to a specific major version, you can run:
::: code-group Updating Sveltia CMS via NPM
```bash [npm]
npm install @sveltia/cms@^1
```
```bash [yarn]
yarn add @sveltia/cms@^1
```
```bash [pnpm]
pnpm add @sveltia/cms@^1
```
```bash [bun]
bun add @sveltia/cms@^1
```
:::
See the [NPM semantic versioning documentation](https://docs.npmjs.com/about-semantic-versioning) for more details on version ranges.
---
---
url: /en/docs/fields/richtext.md
description: Create and format rich content in Sveltia CMS with the Lexical editor.
---
# RichText Field
The RichText field type provides a rich text editor that supports Markdown content. It allows content editors to format text, add links, images, and other media, making it a versatile choice for creating rich content.
::: tip Note for Netlify/Decap CMS users
For backward compatibility with Netlify/Decap CMS, the [Markdown](/en/docs/fields/markdown) field type remains available as an alias of the RichText field type. You can use either `richtext` or `markdown` as the `widget` value in your field configuration.
:::
## User Interface
### Editor
A [Lexical](https://lexical.dev/)-based rich text editor, including headings, lists, links, images, code blocks, and more. It provides a user-friendly interface for writing and formatting content.
The built-in toolbar includes buttons for common formatting options, which can be customized using the `buttons` option. The editor also supports different modes, including a raw Markdown editing mode, which can be configured using the `modes` option. Additional editor components can be added to enhance the editing experience using the `editor_components` option.
Local/remote images can be pasted or dropped into the editor to insert them. Note: pasting multiple images is [not supported in Firefox](https://bugzilla.mozilla.org/show_bug.cgi?id=864052).
::: warning Breaking change from Netlify/Decap CMS
Remark plugins are not supported because Sveltia CMS uses the Lexical framework instead of Slate. The `CMS.registerRemarkPlugin` API method is a noop in Sveltia CMS.
:::
### Preview
A read-only view of the rich text content, rendered as HTML.
## Data Type
A Markdown string. See the [Data Output](/en/docs/data-output#markdown-syntax) documentation for details on the Markdown syntax used by Lexical.
If the `required` option is set to `false` and the field is left empty, the value will be an empty string.
You need to parse the Markdown string using a Markdown parser in your framework to convert it to HTML for rendering on your website. Some frameworks have built-in support for Markdown, while others may require additional libraries. Please refer to your framework’s documentation on how to handle Markdown content. See also the [how-to](/en/docs/how-tos#rendering-soft-line-breaks-as-hard-line-breaks-in-markdown) for advice on handling line breaks in Markdown.
::: info Future Plans
We plan to add support for HTML output in future releases. It will provide additional features specific to HTML content, including text alignment, link targets, and more.
:::
## Data Validation
* If the `required` option is set to `true`, the rich text content must not be an empty string.
* If the `pattern` option is provided, the rich text content must match the specified regular expression pattern.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the Markdown field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `richtext`.
### Optional Options
::: warning Breaking changes from Netlify/Decap CMS
Sveltia CMS has changed the default value of the `sanitize_preview` option to `true` for improved security. In Netlify/Decap CMS, the default is `false`, which may expose users to XSS vulnerabilities.
Also, Sveltia CMS does not support the deprecated camelCase `editorComponents` option. Use `editor_components` instead.
:::
#### `default`
* **Type**: `string`
* **Default**: `""`
The default content for the field. The format should match the selected `format` option.
#### `minimal`
* **Type**: `boolean`
* **Default**: `false`
Whether to limit the editor height. When set to `true`, the editor height is reduced and a scrollbar appears when the content exceeds the height.
#### `modes`
* **Type**: `array`
* **Default**: `[rich_text, raw]`
The modes available in the editor. Possible values are `rich_text` and `raw`. The `raw` mode allows users to edit the raw Markdown text.
The following configurations are possible:
* Default modes: `[rich_text, raw]`
* Turn on raw mode by default: `[raw, rich_text]`
* Rich text only: `[rich_text]`
* Raw mode only: `[raw]`
If multiple modes are enabled, users can switch between them using a mode selector in the editor toolbar.
#### `buttons`
* **Type**: `array`
* **Default**: all available buttons (see below)
The button names to display in the editor toolbar.
The following `buttons` are available in the rich text editor toolbar:
* Inline formatting: `bold`, `italic`, `strikethrough`, `code`, `link`
* Block types: `heading-one`, `heading-two`, `heading-three`, `heading-four`, `heading-five`, `heading-six`, `bulleted-list`, `numbered-list`, `quote`
By default, all buttons are enabled. You can customize the toolbar by specifying the desired buttons in the `buttons` option.
::: tip Note for Netlify/Decap CMS users
Unlike Netlify/Decap CMS, all the block type buttons are available under the block type selector in Sveltia CMS. Users can select the block type from a dropdown menu rather than having separate buttons for each block type.
:::
::: info Future Plans
These buttons are disabled when `raw` mode is active. This behavior may be changed in future releases to allow certain buttons to function in `raw` mode as well.
:::
#### `editor_components`
* **Type**: `array`
* **Default**: `[code-block, image]`
The editor component names to include in the rich text editor.
Editor components are custom blocks that can be inserted into the content. Sveltia CMS includes built-in components and also allows for custom components.
Sveltia CMS includes the following built-in editor components for the RichText field:
* `code-block`: Allows users to insert and format code blocks with syntax highlighting.
* `image`: Enables users to add images to their content, with support for uploading and selecting images from the media storage. The image can be linked or unlinked based on the `linked_images` option.
Both are enabled by default. You can disable them by omitting them from the `editor_components` option.
::: tip Note for Netlify/Decap CMS users
Unlike Netlify/Decap CMS, the `code-block` component in Sveltia CMS is implemented as a block type. Users can insert it using the block type selector rather than the insert button. Also, the `image` component is displayed as a separate button in the toolbar for easier access.
:::
::: info Future Plans
More built-in editor components may be added in future releases, such as `table`.
:::
Developers can create [custom editor components](/en/docs/api/editor-components) to extend the functionality of the rich text editor. Custom components can be registered globally in Sveltia CMS.
#### `linked_images`
* **Type**: `boolean`
* **Default**: `true`
Whether to allow linking images in the editor. When set to `true`, users can add links to images. When set to `false`, images will be inserted without links.
#### `sanitize_preview`
* **Type**: `boolean`
* **Default**: `true`
Whether to sanitize the preview content to prevent [cross-site scripting](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS) (XSS) attacks. The sanitization process uses [DOMPurify](https://github.com/cure53/DOMPurify) to remove potentially harmful HTML tags and attributes from the content before rendering the preview.
::: danger Security Risk
Setting the `sanitize_preview` option to `false` can expose your CMS to XSS vulnerabilities if untrusted users have access to the CMS, especially when using [Open Authoring](/en/docs/workflows/open). Malicious users could inject harmful scripts into the content, which would then be executed in the browsers of anyone viewing the preview.
We recommend keeping this option enabled unless disabling it fixes a broken preview and you fully trust all users of your CMS or you’re the sole user.
:::
## Examples
### Standard Markdown Field
This example shows a basic Markdown editor with default settings.
::: code-group
```yaml [YAML]
- name: body
label: Body
widget: richtext
```
```toml [TOML]
[[fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]
{
"name": "body",
"label": "Body",
"widget": "richtext"
}
```
```js [JavaScript]
{
name: "body",
label: "Body",
widget: "richtext",
}
```
:::
### Basic Markdown Field with Limited Buttons
This example shows a minimal Markdown editor with only bold, italic, and link buttons.
::: code-group
```yaml [YAML]
- name: content
label: Content
widget: richtext
default: To get started, write your **Markdown** content here.
minimal: true
buttons: [bold, italic, link]
```
```toml [TOML]
[[fields]]
name = "content"
label = "Content"
widget = "richtext"
default = "To get started, write your **Markdown** content here."
minimal = true
buttons = ["bold", "italic", "link"]
```
```json [JSON]
{
"name": "content",
"label": "Content",
"widget": "richtext",
"default": "To get started, write your **Markdown** content here.",
"minimal": true,
"buttons": ["bold", "italic", "link"]
}
```
```js [JavaScript]
{
name: "content",
label: "Content",
widget: "richtext",
default: "To get started, write your **Markdown** content here.",
minimal: true,
buttons: ["bold", "italic", "link"],
}
```
:::
### Disabling Code Block Component
This example shows how to disable the `code-block` editor component.
::: code-group
```yaml [YAML]
- name: content
label: Content
widget: richtext
editor_components: [image]
```
```toml [TOML]
[[fields]]
name = "content"
label = "Content"
widget = "richtext"
editor_components = ["image"]
```
```json [JSON]
{
"name": "content",
"label": "Content",
"widget": "richtext",
"editor_components": ["image"]
}
```
```js [JavaScript]
{
name: "content",
label: "Content",
widget: "richtext",
editor_components: ["image"],
}
```
:::
---
---
url: /en/docs/roadmap.md
description: >-
View the Sveltia CMS roadmap with upcoming features, planned releases, and
development priorities.
---
# Roadmap
As the [true successor to Netlify CMS](/en/docs/successor-to-netlify-cms), our goal is to address all relevant and fixable [Netlify/Decap CMS issues](https://github.com/decaporg/decap-cms/issues) over the course of this project. We also have lots of ideas to make Sveltia CMS a great product.
We cannot promise any specific release dates, but here is a rough roadmap for the next few years.
## Upcoming Releases
### v1.0
Expected early 2026. This will be the first stable release of Sveltia CMS, suitable for production use in single-user scenarios. This release also [solves 300 issues](/en/docs/successor-to-netlify-cms#tackling-as-many-netlify-decap-cms-issues-as-possible) of Netlify/Decap CMS, including many highly requested features and important bug fixes.
* Enhanced [compatibility with Netlify/Decap CMS](/en/docs/migration/netlify-decap-cms#current-limitations)
* Tackling some more Netlify/Decap CMS issues, including:
* PKCE for GitHub\[^285] — We are waiting for GitHub to support client-side PKCE authentication for single-page apps. It was [planned for Q4 2025](https://github.com/github/roadmap/issues/1153), but there is [no update](https://github.com/orgs/community/discussions/15752) since then. Sveltia CMS v1.0 will not be released until this is supported.
* [Amazon S3 and Cloudflare R2 media storage providers](https://github.com/sveltia/sveltia-cms/issues/586)\[^315]
* Thorough CMS config validation\[^246]
* [Entry pre-validation/normalization](https://github.com/sveltia/sveltia-cms/issues/395)\[^248]
* Accessibility audit
* [Localization](https://github.com/sveltia/sveltia-cms/blob/main/src/lib/locales/README.md)
* [Live demo site](https://github.com/sveltia/sveltia-cms/issues/1)
See also the [1.0 RC](https://github.com/sveltia/sveltia-cms/milestone/1) and [1.0](https://github.com/sveltia/sveltia-cms/milestone/2) milestones for a list of issues planned for v1.0.
### v2.0
Expected mid-2026. This release completes the core features of Sveltia CMS as a successor to Netlify/Decap CMS.
* Implementing [a few deferred features](/en/docs/migration/netlify-decap-cms#current-limitations) from Netlify/Decap CMS, including editorial workflow and nested collections, while addressing a number of bugs in their implementations
* They will probably first be included as beta features in v1.x releases without localization
* Tackling even more Netlify/Decap CMS issues, including:
* [Manual entry sorting](https://github.com/sveltia/sveltia-cms/issues/214)\[^125]
* [Directory navigation in the Asset Library](https://github.com/sveltia/sveltia-cms/issues/420)\[^240]
* [Asset collections](https://github.com/sveltia/sveltia-cms/issues/301)\[^271]
See also the [2.0](https://github.com/sveltia/sveltia-cms/milestone/3) milestone.
### v3.0
Expected late 2026. This release provides an alternative to the deprecated Netlify Identity and Git Gateway services used by Netlify/Decap CMS. It also officially supports multi-user scenarios for small to medium teams.
* **Sveltia CMS Additions**: Edge functions for Cloudflare Workers and possibly other platforms that provide features that cannot be implemented client-side:
* User management (Netlify Identity alternative) with roles\[^23]
* Commits without a Git service account (Git Gateway alternative)
* Post locking\[^166] for avoiding conflicts (like [WordPress](https://codex.wordpress.org/Post_Locking))
* More enhancements are planned for the future; see the TBD list below
* Tackling even more Netlify/Decap CMS issues
## TBD
* Enhancements to **Sveltia CMS Additions** (some may be included in v3.0):
* Scheduled posts\[^167]
* [Private configuration](https://github.com/sveltia/sveltia-cms/discussions/576)
* [Credential management](https://github.com/sveltia/sveltia-cms/issues/444) for service API keys, deploy hook URL, etc.
* Proxy for services that don’t support [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS):
* [DeepL Translate](https://github.com/sveltia/sveltia-cms/issues/437)
* [Git LFS support for GitHub](https://github.com/sveltia/sveltia-cms/discussions/353)\[^244]
* Tackling most of the remaining Netlify/Decap CMS issues (some may be included in v2.0 or v3.0):
* Their [top-voted features](https://github.com/decaporg/decap-cms/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc), including:
* MDX support\[^122]
* [Preview Workflow](https://github.com/sveltia/sveltia-cms/discussions/440)\[^261]
* [Tables in Markdown](https://github.com/sveltia/sveltia-cms/issues/455)\[^256]
* [Config editor](https://github.com/sveltia/sveltia-cms/discussions/452)\[^10]
* [Theming](https://github.com/sveltia/sveltia-cms/issues/29)\[^262]
* [Reusable field groups](https://github.com/sveltia/sveltia-cms/discussions/463)\[^263]
* Offline support\[^238]
* Features to turn the CMS into a powerful NoSQL database management system (DBMS):
* Advanced Relation fields\[^242]
* Cascade updates/deletes\[^243]
* [Quick item additions](https://github.com/sveltia/sveltia-cms/issues/493)\[^266]
* Autoincrement fields\[^286]
* Unique constraints\[^287]
* Data integrity checks
* [Automatic asset file renaming with templates](https://github.com/sveltia/sveltia-cms/issues/422)\[^241]
* [Tabbed interface for field groups](https://github.com/sveltia/sveltia-cms/discussions/592)\[^400]
* and many more (50+ issues and discussions)
* Content editor enhancements:
* Field navigator
* Sidebar
* View, compare and restore revisions (like [WordPress](https://wordpress.com/support/page-post-revisions/))
* [Reverse reference lists](https://github.com/sveltia/sveltia-cms/discussions/416)
* Validation errors
* Input UI improvements
* Slider input for number fields
* Custom color picker
* Custom date/time picker
* Image field enhancements:
* Camera input, screen capture, QR code generation, AI image generation, etc.
* Quick cropping and resizing
* Search enhancements:
* Customizable search fields\[^274]
* Advanced search options
* [Fuzzy search](https://www.fusejs.io/)
* [Local repository workflow](/en/docs/workflows/local) improvements: Git mode\[^131] and change detection
* [Preact+HTM](https://github.com/sveltia/sveltia-cms/discussions/153) or Vue support for custom field types, editor components and preview templates\[^289]
* More integration options: stock photos, stock videos, cloud storage providers, translation services, maps, analytics tools, etc.
* More AI features for image generation, content writing, etc.
* Advanced digital asset management (DAM) features, including image editing and tagging\[^114]
* Marketplace for custom field types, etc.\[^273]
* Official starter templates for the most popular frameworks, including Astro, Eleventy and Hugo
* Contributor documentation
* End-user documentation
* and so much more!
## Non-Goals
* Serving 100% of the existing Netlify/Decap CMS user base. While we aim to support most use cases, some users may be unable to migrate to Sveltia CMS due to unsupported features or technical limitations. See the [Compatibility](/en/docs/migration/netlify-decap-cms#compatibility) section for details.
* Support for non-Git backends. Sveltia CMS is a Git-based headless CMS and will remain so to avoid feature creep and increased maintenance costs. We may support custom backends through an API in the future, but it’s not a priority right now.
* Framework-specific integrations, including a WYSIWYG editor and out-of-the-box live site preview. We will focus on framework-agnostic core features that are essential for succeeding Netlify/Decap CMS and modernizing the platform.
* Enterprise features. We want to keep Sveltia CMS simple and easy to use for small teams and individual developers.
* Monetization. We may offer an affordable cloud version in the future since self-hosting the CMS can be a hassle. However, we will not charge for the CMS itself. We want to keep it free and open source forever.
\[^285]: Netlify/Decap CMS [#6597](https://github.com/decaporg/decap-cms/issues/6597)
\[^246]: Netlify/Decap CMS [#1074](https://github.com/decaporg/decap-cms/issues/1074), [#1693](https://github.com/decaporg/decap-cms/issues/1693), [#3803](https://github.com/decaporg/decap-cms/issues/3803)
\[^248]: Netlify/Decap CMS [#836](https://github.com/decaporg/decap-cms/issues/836), [#3524](https://github.com/decaporg/decap-cms/issues/3524)
\[^125]: Netlify/Decap CMS [#475](https://github.com/decaporg/decap-cms/issues/475), [#5469](https://github.com/decaporg/decap-cms/issues/5469)
\[^240]: Netlify/Decap CMS [#2113](https://github.com/decaporg/decap-cms/issues/2113), [#3240](https://github.com/decaporg/decap-cms/issues/3240)
\[^271]: Netlify/Decap CMS [#1046](https://github.com/decaporg/decap-cms/issues/1046)
\[^23]: Netlify/Decap CMS [#2](https://github.com/decaporg/decap-cms/issues/2), [#2319](https://github.com/decaporg/decap-cms/issues/2319), [#6216](https://github.com/decaporg/decap-cms/discussions/6216), [#6933](https://github.com/decaporg/decap-cms/discussions/6933)
\[^122]: Netlify/Decap CMS [#1776](https://github.com/decaporg/decap-cms/issues/1776), [#2064](https://github.com/decaporg/decap-cms/issues/2064), [#7158](https://github.com/decaporg/decap-cms/issues/7158), [#7259](https://github.com/decaporg/decap-cms/issues/7259)
\[^261]: Netlify/Decap CMS [#942](https://github.com/decaporg/decap-cms/issues/942)
\[^256]: Netlify/Decap CMS [#2845](https://github.com/decaporg/decap-cms/issues/2845)
\[^10]: Netlify/Decap CMS [#341](https://github.com/decaporg/decap-cms/issues/341), [#1167](https://github.com/decaporg/decap-cms/issues/1167)
\[^262]: Netlify/Decap CMS [#1727](https://github.com/decaporg/decap-cms/issues/1727)
\[^263]: Netlify/Decap CMS [#1342](https://github.com/decaporg/decap-cms/issues/1342)
\[^238]: Netlify/Decap CMS [#1502](https://github.com/decaporg/decap-cms/issues/1502)
\[^242]: Netlify/Decap CMS [#192](https://github.com/decaporg/decap-cms/issues/192)
\[^243]: Netlify/Decap CMS [#717](https://github.com/decaporg/decap-cms/issues/717), [#5750](https://github.com/decaporg/decap-cms/issues/5750), [#6895](https://github.com/decaporg/decap-cms/issues/6895)
\[^266]: Netlify/Decap CMS [#3821](https://github.com/decaporg/decap-cms/issues/3821), [#4369](https://github.com/decaporg/decap-cms/issues/4369)
\[^286]: Netlify/Decap CMS [#2367](https://github.com/decaporg/decap-cms/issues/2367)
\[^287]: Netlify/Decap CMS [#1069](https://github.com/decaporg/decap-cms/issues/1069)
\[^241]: Netlify/Decap CMS [#857](https://github.com/decaporg/decap-cms/issues/857)
\[^166]: Netlify/Decap CMS [#277](https://github.com/decaporg/decap-cms/issues/277), [#1500](https://github.com/decaporg/decap-cms/issues/1500)
\[^167]: Netlify/Decap CMS [#263](https://github.com/decaporg/decap-cms/issues/263), [#2034](https://github.com/decaporg/decap-cms/issues/2034)
\[^244]: Netlify/Decap CMS [#1206](https://github.com/decaporg/decap-cms/issues/1206)
\[^274]: Netlify/Decap CMS [#549](https://github.com/decaporg/decap-cms/issues/549)
\[^131]: Netlify/Decap CMS [#4429](https://github.com/decaporg/decap-cms/issues/4429)
\[^289]: Netlify/Decap CMS [#2183](https://github.com/decaporg/decap-cms/issues/2183)
\[^114]: Netlify/Decap CMS [#5029](https://github.com/decaporg/decap-cms/issues/5029), [#5048](https://github.com/decaporg/decap-cms/issues/5048)
\[^273]: Netlify/Decap CMS [#919](https://github.com/decaporg/decap-cms/issues/919), [#936](https://github.com/decaporg/decap-cms/issues/936), [#1286](https://github.com/decaporg/decap-cms/issues/1286), [#1288](https://github.com/decaporg/decap-cms/issues/1288), [#1400](https://github.com/decaporg/decap-cms/issues/1400)
\[^315]: Netlify/Decap CMS [#2035](https://github.com/decaporg/decap-cms/issues/2035), [#7362](https://github.com/decaporg/decap-cms/issues/7362), [#7543](https://github.com/decaporg/decap-cms/issues/7543)
\[^400]: Netlify/Decap CMS [#908](https://github.com/decaporg/decap-cms/issues/908), [#1484](https://github.com/decaporg/decap-cms/issues/1484), [#1759](https://github.com/decaporg/decap-cms/issues/1759)
---
---
url: /en/docs/security.md
description: >-
Secure Sveltia CMS with built-in security features, best practices, and
configuration recommendations.
---
# Security
Security is a top priority for Sveltia CMS. This document outlines the security features of Sveltia CMS and provides best practices for securing your CMS installation.
## Our Approach
Sveltia CMS employs multiple layers of security measures to protect your data and ensure a safe content management experience. Our security approach includes the following features and practices:
### Security Features
* **XSS protection** - The [unpatched XSS vulnerability](https://github.com/advisories/GHSA-xp8g-32qh-mv28) in Decap CMS does not affect Sveltia CMS. The `sanitize_preview` [RichText field](/en/docs/fields/richtext) option defaults to `true`.
* **No proxy required** - The [local workflow](/en/docs/workflows/local) eliminates attack surfaces from compromised dependencies and unauthorized API access.
* **Secure contexts only** - HTTPS is required for all site content and CMS configuration.
* **Automatic security headers** - The `same-origin` referrer policy is automatically set.
* **Simplified CSP** - No `unsafe-eval` or `unsafe-inline` needed in `script-src`.
* **Signed commits** - GitHub commits are automatically GPG-signed and [verified](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification).
* **Signed uploads** - Media uploads to Uploadcare are signed using secure API keys.
### Security Practices
* **Dependency security** - Constant updates, Dependabot alerts, and [`pnpm audit`](https://pnpm.io/cli/audit) prevent vulnerabilities. We use version [`cooldown`](https://github.com/raineorshine/npm-check-updates#cooldown) and [`minimumReleaseAge`](https://pnpm.io/settings#minimumreleaseage) to protect against supply chain attacks.
* **Transparent releases** - We use pnpm, Vite, GitHub Actions, and [npm package provenance](https://github.blog/security/supply-chain-security/introducing-npm-package-provenance/) for verifiable, reliable releases.
* **Publishing security** - [Trusted publishing](https://docs.npmjs.com/trusted-publishers) and [2FA](https://docs.npmjs.com/requiring-2fa-for-package-publishing-and-settings-modification) enabled.
* **Security policy** - Published [security policy](https://github.com/sveltia/sveltia-cms/blob/main/SECURITY.md) for responsible disclosure.
## What You Can Do
To protect your Sveltia CMS installation and data, follow these best practices:
* Keep your Sveltia CMS installation [up to date](/en/docs/releases). If you use the CDN version, you’ll always get the latest version unless you specify an exact version number in the URL. If you self-host, regularly check for updates and apply them promptly.
* Set up PKCE authentication for your Git backend. Sveltia CMS supports quick PAT authentication, but it’s mainly for individual developers and not recommended for multi-user teams, especially when non-technical members are involved.
* Set up two-factor authentication (2FA) for your Git instance.
* Keep your Git instance up to date if you’re self-hosting it.
* Use HTTPS for your site to ensure secure communication between the client and server. All major hosting providers use HTTPS by default. If you self-host, consider using [Let’s Encrypt](https://letsencrypt.org/) to obtain free TLS certificates. Sveltia CMS doesn’t work on HTTP sites.
* Set up Content Security Policy (CSP) for your site. See the section below for recommended policies.
* Do not disable the `sanitize_preview` option for [RichText](/en/docs/fields/richtext) and [Markdown](/en/docs/fields/markdown) fields unless you fully understand the implications. Disabling this option may expose your site to XSS attacks if untrusted users can edit content.
## Setting up Content Security Policy
If your site adopts Content Security Policy (CSP), use the following policy for Sveltia CMS, or some features may not work.
```
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' blob: data:;
media-src blob:;
frame-src blob:;
script-src 'self' https://unpkg.com;
connect-src 'self' blob: data: https://unpkg.com;
```
::: tip Note for Netlify/Decap CMS users
Sveltia CMS does not require the `unsafe-eval` and `unsafe-inline` keywords in the `script-src` CSP directive. Also, the `script-src` CSP directive is not required for the Cloudinary integration to work, as we implemented it without using their hosted widget script.
:::
::: tip Allowing Image URLs
If you have image field(s) and expect that images will be inserted as URLs, you may want to allow any source using a wildcard instead of specifying individual origins:
```
img-src 'self' blob: data: https://*;
```
:::
::: info About UNPKG origin
[UNPKG](https://unpkg.com/) is used not only to download the CMS script bundle, but also to check for the latest version and retrieve additional dependencies such as [PDF.js](https://github.com/mozilla/pdf.js) and [Prism](https://prismjs.com/) language definitions.
:::
Then, add the following origins depending on your Git backend and enabled integrations.
### Backends
#### GitHub
If you’re running a GitHub Enterprise Server, you’ll also need to add the origin to these directives.
* `img-src`
```
https://*.githubusercontent.com
```
* `connect-src`
```
https://api.github.com https://www.githubstatus.com
```
#### GitLab
If you’re running a self-hosted instance, you’ll also need to add the origin to these directives.
* `img-src`
```
https://gitlab.com https://secure.gravatar.com
```
* `connect-src`
```
https://gitlab.com https://status-api.hostedstatus.com
```
#### Gitea/Forgejo
If you’re running a self-hosted instance, use the origin instead.
* `img-src`
```
https://gitea.com
```
* `connect-src`
```
https://gitea.com
```
### Map Providers
#### OpenStreetMap
* `img-src`
```
https://*.openstreetmap.org
```
* `connect-src`
```
https://*.openstreetmap.org
```
### Media Storage Providers
#### Cloudinary
* `img-src`
```
https://res.cloudinary.com
```
or a custom domain if configured
* `frame-src`
```
https://console.cloudinary.com
```
#### Uploadcare
* `img-src`
```
https://*.ucarecd.net https://ucarecdn.com
```
or a custom domain if configured
* `connect-src`
```
https://upload.uploadcare.com https://api.uploadcare.com
```
### Stock Photo Providers
#### Pexels
* `img-src`
```
https://images.pexels.com
```
* `connect-src`
```
https://images.pexels.com https://api.pexels.com
```
#### Pixabay
* `img-src`
```
https://pixabay.com
```
* `connect-src`
```
https://pixabay.com
```
#### Unsplash
* `img-src`
```
https://images.unsplash.com
```
* `connect-src`
```
https://images.unsplash.com https://api.unsplash.com
```
### AI Integrations
#### Google Cloud Translation
* `connect-src`
```
https://translation.googleapis.com
```
#### Google Gemini
* `connect-src`
```
https://generativelanguage.googleapis.com
```
#### Anthropic
* `connect-src`
```
https://api.anthropic.com
```
#### OpenAI
* `connect-src`
```
https://api.openai.com
```
### Video Embeds
#### YouTube
* `frame-src`
```
https://www.youtube-nocookie.com
```
### CI/CD Providers
If you choose to [disable automatic deployments](/en/docs/deployments#disabling-automatic-deployments) and have configured a webhook URL, you may need to add the origin to the `connect-src` directive. Here are some examples:
#### Cloudflare Pages
* `connect-src`
```
https://api.cloudflare.com
```
#### Netlify
* `connect-src`
```
https://api.netlify.com
```
#### Vercel
* `connect-src`
```
https://api.vercel.com
```
---
---
url: /en/docs/fields/select.md
description: Choose single or multiple options in Sveltia CMS from predefined lists.
---
# Select Field
The Select field type allows users to choose one or more options from a predefined list within the CMS entry form.
::: tip Alternative for dynamic or boolean selects
The options in a Select field are meant to be a static list of a small number of choices defined in the configuration file. If you need dynamic options based on other collections, consider using the [Relation](/en/docs/fields/relation) field type instead. See also our [how-to guide](/en/docs/how-tos#using-entry-tags-for-categorization) on using entry tags for categorization.
For boolean (true/false) selections, consider using the [Boolean](/en/docs/fields/boolean) field type.
:::
## User Interface
### Editor
Radio buttons (single select) or checkboxes (multi select) for choosing options. 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.
### Preview
A string or a list of strings representing the selected option(s).
## Data Type
It depends on the `options`. Usually a string or an array of strings, depending on whether the `multiple` option is set to `true` or `false`, but can also be a number or an array of numbers if the options are defined as such.
If the `required` option is set to `false` and no option is selected, the value will be `null` for single select or an empty array for multi select.
## Data Validation
* If the `required` option is set to `true`, at least one option must be selected.
* If the `multiple` option is enabled, the number of selected options must be between the `min` and `max` limits, if specified.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the Select field supports the following options:
### Required Options
#### `widget`
* **Type**: `string`
* **Default**: `string` The `options` can be defined with custom labels and values. The following example shows a select field for choosing a color with specific hex values. Must be set to `select`.
#### `options`
* **Type**: `array`
* **Default**: `[]`
An array of options for the select field. Each option can be defined as a string/number or as an object with `label` and `value` properties. These options will be presented to the user in the UI.
The following are valid examples of the `options` configuration:
::: code-group
```yaml [YAML]
options:
- London
- Paris
- New York
```
```toml [TOML]
options = ["London", "Paris", "New York"]
```
```json [JSON]
{
"options": ["London", "Paris", "New York"]
}
```
```js [JavaScript]
options: ['London', 'Paris', 'New York'],
```
:::
::: code-group
```yaml [YAML]
options: [1, 2, 3]
```
```toml [TOML]
options = [1, 2, 3]
```
```json [JSON]
{
"options": [1, 2, 3]
}
```
```js [JavaScript]
options: [1, 2, 3],
```
:::
::: code-group
```yaml [YAML]
options:
- { label: Toronto, value: YYZ }
- { label: Vancouver, value: YVR }
- { label: Montreal, value: YUL }
```
```toml [TOML]
[[options]]
label = "Toronto"
value = "YYZ"
[[options]]
label = "Vancouver"
value = "YVR"
[[options]]
label = "Montreal"
value = "YUL"
```
```json [JSON]
{
"options": [
{ "label": "Toronto", "value": "YYZ" },
{ "label": "Vancouver", "value": "YVR" },
{ "label": "Montreal", "value": "YUL" }
]
}
```
```js [JavaScript]
options: [
{ label: 'Toronto', value: 'YYZ' },
{ label: 'Vancouver', value: 'YVR' },
{ label: 'Montreal', value: 'YUL' },
],
```
:::
::: code-group
```yaml [YAML]
options:
- { label: Red, value: 1 }
- { label: Green, value: 2 }
- { label: Blue, value: 3 }
```
```toml [TOML]
[[options]]
label = "Red"
value = 1
[[options]]
label = "Green"
value = 2
[[options]]
label = "Blue"
value = 3
```
```json [JSON]
{
"options": [
{ "label": "Red", "value": 1 },
{ "label": "Green", "value": 2 },
{ "label": "Blue", "value": 3 }
]
}
```
```js [JavaScript]
options: [
{ label: 'Red', value: 1 },
{ label: 'Green', value: 2 },
{ label: 'Blue', value: 3 },
],
```
:::
### Optional Options
#### `default`
* **Type**: `string`, `number`, `array of strings`, or `array of numbers`
* **Default**: `null` or `[]`
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.
#### `dropdown_threshold`
* **Type**: `integer`
* **Default**: `5`
The number of options at which to switch from radio buttons/checkboxes to a dropdown UI. If the number of options exceeds this threshold, a dropdown with search functionality will be used.
#### `multiple`
* **Type**: `boolean`
* **Default**: `false`
Whether to allow selecting multiple options.
#### `min`
* **Type**: `integer`
* **Default**: `0`
The minimum number of options required. This enables validation to ensure that users select at least this many options. Ignored if `multiple` is set to `false`.
#### `max`
* **Type**: `integer`
* **Default**: `Infinity`
The maximum number of options allowed. This enables validation to prevent users from selecting more than this many options. Ignored if `multiple` is set to `false`.
## Examples
### Single Select
The following example shows a basic single select field for choosing a country.
::: code-group
```yaml [YAML]
- name: country
label: Country
widget: select
options:
- USA
- Canada
- Mexico
```
```toml [TOML]
[[fields]]
name = "country"
label = "Country"
widget = "select"
options = ["USA", "Canada", "Mexico"]
```
```json [JSON]
{
"fields": [
{
"name": "country",
"label": "Country",
"widget": "select",
"options": ["USA", "Canada", "Mexico"]
}
]
}
```
```js [JavaScript]
{
fields: [
{
name: 'country',
label: 'Country',
widget: 'select',
options: ['USA', 'Canada', 'Mexico'],
},
],
}
```
:::
Output example when “Canada” is selected:
::: code-group
```yaml [YAML]
country: Canada
```
```toml [TOML]
country = "Canada"
```
```json [JSON]
{
"country": "Canada"
}
```
:::
### Custom Labels and Values
The `options` can be defined with custom labels and values. The following example shows a select field for choosing a color with specific hex values.
::: code-group
```yaml [YAML]
- name: color
label: Color
widget: select
options:
- { label: Red, value: '#FF0000' }
- { label: Green, value: '#00FF00' }
- { label: Blue, value: '#0000FF' }
```
```toml [TOML]
[[fields]]
name = "color"
label = "Color"
widget = "select"
[[options]]
label = "Red"
value = "#FF0000"
[[options]]
label = "Green"
value = "#00FF00"
[[options]]
label = "Blue"
value = "#0000FF"
```
```json [JSON]
{
"fields": [
{
"name": "color",
"label": "Color",
"widget": "select",
"options": [
{ "label": "Red", "value": "#FF0000" },
{ "label": "Green", "value": "#00FF00" },
{ "label": "Blue", "value": "#0000FF" }
]
}
]
}
```
```js [JavaScript]
{
fields: [
{
name: 'color',
label: 'Color',
widget: 'select',
options: [
{ label: 'Red', value: '#FF0000' },
{ label: 'Green', value: '#00FF00' },
{ label: 'Blue', value: '#0000FF' },
],
},
],
}
```
:::
Output example when “Green” is selected:
::: code-group
```yaml [YAML]
color: '#00FF00'
```
```toml [TOML]
color = "#00FF00"
```
```json [JSON]
{
"color": "#00FF00"
}
```
:::
### Multi Select with Limits
The following example shows a multi select field for choosing fruits, with a minimum of 1 and a maximum of 3 selections.
::: code-group
```yaml [YAML]
- name: fruits
label: Fruits
widget: select
options:
- Apple
- Banana
- Cherry
- Date
multiple: true
min: 1
max: 3
```
```toml [TOML]
[[fields]]
name = "fruits"
label = "Fruits"
widget = "select"
options = ["Apple", "Banana", "Cherry", "Date"]
multiple = true
min = 1
max = 3
```
```json [JSON]
{
"fields": [
{
"name": "fruits",
"label": "Fruits",
"widget": "select",
"options": ["Apple", "Banana", "Cherry", "Date"],
"multiple": true,
"min": 1,
"max": 3
}
]
}
```
```js [JavaScript]
{
fields: [
{
name: 'fruits',
label: 'Fruits',
widget: 'select',
options: ['Apple', 'Banana', 'Cherry', 'Date'],
multiple: true,
min: 1,
max: 3,
},
],
}
```
:::
Output example when “Apple” and “Cherry” are selected:
::: code-group
```yaml [YAML]
fruits:
- Apple
- Cherry
```
```toml [TOML]
fruits = ["Apple", "Cherry"]
```
```json [JSON]
{
"fruits": ["Apple", "Cherry"]
}
```
:::
---
---
url: /en/showcase.md
description: >-
Explore real-world websites using Sveltia CMS across various industries, built
with Astro, Eleventy, Hugo, Jekyll, SvelteKit and other frameworks.
---
# Showcase
Sveltia CMS powers hundreds of websites around the world. Here are some hand-picked examples, updated daily. Find inspiration for your next project, and see how Sveltia CMS can be used in different contexts and with various frameworks.
\[
{
"name": "Inclusive Design Research Centre",
"url": "https://idrc.ocadu.ca/",
"description": "A research center at OCAD University focused on inclusive design practices.",
"country": "CA",
"framework": "eleventy",
"categories": \["education"],
"links": \[
{
"type": "repository",
"url": "https://github.com/inclusive-design/idrc"
}
]
},
{
"name": "DIVD",
"url": "https://www.divd.nl/",
"description": "An institute focused on responsible vulnerability disclosure in the Netherlands.",
"country": "NL",
"framework": "hugo",
"categories": \["science", "community"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/DIVD-NL/web-www-v2"
}
]
},
{
"name": "Mike Masnick",
"url": "https://masnick.com/",
"description": "Personal website of Mike Masnick, founder and editor of Techdirt and a board member of Bluesky.",
"country": "US",
"framework": "hugo",
"migratedFrom": "wordpress",
"categories": \["personal"],
"links": \[
{
"type": "social-post",
"url": "https://bsky.app/profile/masnick.com/post/3mdt7pcsds224"
},
{
"type": "blog-post",
"url": "https://masnick.com/posts/2026-02-01-how-this-was-built-how-it-works/"
}
]
},
{
"name": "Anil Dash",
"url": "https://www.anildash.com/",
"description": "Personal blog of Anil Dash, a prominent tech writer and entrepreneur, who created a once-popular CMS called Movable Type.",
"country": "US",
"framework": "eleventy",
"categories": \["personal", "media"],
"links": \[
{
"type": "social-post",
"url": "https://bsky.app/profile/anildash.com/post/3m7osuyipnc22"
}
]
},
{
"name": "Medieval Scrolls Digital Archive",
"url": "https://medievalscrolls.fas.harvard.edu/",
"description": "A comprehensive resource for medieval scrolls at Harvard University.",
"country": "US",
"framework": "astro",
"categories": \["education", "reference"],
"links": \[
{
"type": "repository",
"url": "https://github.com/artshumrc/scrolls-astro"
}
]
},
{
"name": "Massa Crítica Portugal",
"url": "https://massacritica.pt/pt/",
"description": "The Portuguese chapter of the global Critical Mass movement promoting cycling.",
"country": "PT",
"framework": "astro",
"categories": \["community"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/afonsojramos/critical-mass"
}
]
},
{
"name": "The Forgotten Europe Project",
"url": "https://forgotteneurope.org/",
"description": "A project dedicated to exploring and documenting lesser-known parts of Europe.",
"country": "US",
"framework": "sveltekit",
"categories": \["education"],
"links": \[
{
"type": "repository",
"url": "https://github.com/refact0r/forgotten-europe-website"
}
]
},
{
"name": "Dylan Beattie",
"url": "https://dylanbeattie.net/",
"description": "Personal website of Dylan Beattie, a well-known software developer and speaker.",
"country": "GB",
"framework": "jekyll",
"categories": \["personal", "science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/dylanbeattie/dylanbeattie.net"
},
{
"type": "blog-post",
"url": "https://dylanbeattie.net/2025/02/13/sveltiacms-jekyll-and-github-pages.html"
},
{
"type": "social-post",
"url": "https://bsky.app/profile/dylanbeatt.ie/post/3liad2ym4mk24"
}
]
},
{
"name": "FLOE",
"url": "https://floeproject.org/",
"description": "An open education project focused on accessibility and inclusive learning.",
"country": "CA",
"framework": "eleventy",
"categories": \["education"],
"links": \[
{
"type": "repository",
"url": "https://github.com/fluid-project/floeproject.org"
},
{
"type": "blog-post",
"url": "https://floeproject.org/news/2024-06-24-evaluating-static-content-management-systems/"
}
]
},
{
"name": "Bilgin Sözlük",
"url": "https://sozluk.ulug.tr/",
"description": "An etymological dictionary of Turkish.",
"country": "TR",
"framework": "astro",
"categories": \["reference", "education"],
"links": \[
{
"type": "repository",
"url": "https://github.com/ulughann/sozluk"
}
]
},
{
"name": "SIGPwny",
"url": "https://sigpwny.com/",
"description": "A student-run cybersecurity club at the University of Illinois Urbana-Champaign.",
"country": "US",
"framework": "astro",
"categories": \["community", "science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/sigpwny/websites"
}
]
},
{
"name": "CodeBytes",
"url": "https://chris-ayers.com/",
"description": "Personal website of Chris Ayers, a senior software engineer and speaker.",
"country": "US",
"framework": "hugo",
"categories": \["personal", "science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/codebytes/codebytes.github.io"
},
{
"type": "blog-post",
"url": "https://chris-ayers.com/posts/mobile-cms-on-github-pages/"
}
]
},
{
"name": "Woogles Blog",
"url": "https://blog.woogles.io/",
"description": "Blog of a non-profit organization specializing in word games and education.",
"country": "US",
"framework": "hugo",
"categories": \["education", "community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/woogles-io/liwords-static"
}
]
},
{
"name": "The Living Glossary of Digital Narrative",
"url": "https://glossary.cdn.uib.no/",
"description": "A collaborative glossary for digital narrative studies.",
"country": "NO",
"framework": "astro",
"categories": \["education", "reference"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Center-for-Digital-Narrative/Living-Glossary-of-Digital-Narrative"
}
]
},
{
"name": "Melbourne Disc Golf Club",
"url": "https://www.melbournediscgolf.com/",
"description": "A disc golf club in Australia.",
"country": "AU",
"framework": "astro",
"categories": \["community", "sports"],
"links": \[
{
"type": "repository",
"url": "https://github.com/melbourne-disc-golf/mdgc-website"
}
]
},
{
"name": "ACM Gazi",
"url": "https://acmgazi.com/",
"description": "Gazi University Association for Computing Machinery community site.",
"country": "TR",
"framework": "hugo",
"categories": \["community", "education"],
"links": \[
{
"type": "repository",
"url": "https://github.com/ACM-Gazi/acm-gazi.github.io"
}
]
},
{
"name": "Volley Club Nogentais",
"url": "https://volley-club-nogent.fr/",
"description": "A volleyball club in France.",
"country": "FR",
"framework": "hugo",
"categories": \["community", "sports"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Volley-Club-Nogentais/vcn-website"
}
]
},
{
"name": "IA·rbre",
"url": "https://iarbre.fr/",
"description": "A collaborative platform for territorial data on urban climate adaptation.",
"country": "FR",
"framework": "mkdocs",
"categories": \["community", "education"],
"links": \[
{
"type": "repository",
"url": "https://github.com/TelesCoop/iarbre"
}
]
},
{
"name": "The Scalable Way",
"url": "https://thescalableway.com/",
"country": "PL",
"description": "A consultancy specializing in data platform engineering, analytics, and data science.",
"framework": "eleventy",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/dyvenia/site-thescalableway"
}
]
},
{
"name": "Queer Winnipeg",
"url": "https://queerwinnipeg.ca/",
"description": "Blog that shares stories and resources for the local queer community.",
"country": "CA",
"framework": "astro",
"categories": \["media"],
"links": \[
{
"type": "repository",
"url": "https://gitlab.com/queerwinnipeg/queerwinnipeg.ca"
}
]
},
{
"name": "Code for Heilbronn",
"url": "https://codeforheilbronn.de/",
"description": "A civic tech group in Germany, focused on open data and digital solutions for local issues.",
"country": "DE",
"framework": "hugo",
"categories": \["science", "community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/cfhn/codeforheilbronn-website"
}
]
},
{
"name": "Planas B Architektūros Studija",
"url": "https://www.planasb.lt/",
"description": "An architecture studio based in Lithuania.",
"country": "LT",
"framework": "astro",
"categories": \["business"],
"features": \["i18n"],
"links": \[
{
"type": "case-study",
"url": "https://oaksun.studio/work/architectural-studio/"
}
]
},
{
"name": "Alineación indebida",
"url": "https://www.alineacionindebida.com/",
"description": "Sports media outlet covering Salamanca, Spain.",
"country": "ES",
"framework": "astro",
"categories": \["media", "sports"],
"links": \[
{
"type": "repository",
"url": "https://github.com/alineacion-indebida/Web"
}
]
},
{
"name": "Atelier Wehrle",
"url": "https://atelierwehrle.com/",
"description": "A garden design studio based in Germany.",
"country": "DE",
"framework": "eleventy",
"categories": \["business"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/thegrandheavybold/atelierwehrle\_\_com"
}
]
},
{
"name": "LeishMan Network",
"url": "https://leishman-network.org/",
"description": "A European research consortium focused on leishmaniasis.",
"country": "FR",
"framework": "nuxt",
"categories": \["education", "community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/quentinglorieux/LeishMan"
}
]
},
{
"name": "EFA 44",
"url": "https://efa44.org/",
"description": "The Loire-Atlantique branch of the French national federation Enfance & Familles d’Adoption.",
"country": "FR",
"framework": "hugo",
"categories": \["community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/EFA44/site"
}
]
},
{
"name": "VAF HTL Grieskirchen",
"url": "https://absolventen-htlgrieskirchen.at/",
"description": "Alumni association website for HTL Grieskirchen in Austria.",
"country": "AT",
"framework": "hugo",
"categories": \["community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/VAF-HTL-Grieskirchen/vaf-htlgrieskirchen-website"
}
]
},
{
"name": "Irina Dufaud",
"url": "https://illustrasea.com/",
"description": "Portfolio website of Irina Dufaud, an illustrator based in Canada.",
"country": "CA",
"categories": \["arts", "personal"],
"links": \[
{
"type": "repository",
"url": "https://github.com/TheDoubleJo/illustrasea"
}
]
},
{
"name": "FC Aich",
"url": "https://fc-aich.de/",
"description": "A football club based in Germany.",
"country": "DE",
"framework": "hugo",
"categories": \["community", "sports"],
"links": \[
{
"type": "repository",
"url": "https://github.com/fc-aich/fc-aich"
}
]
},
{
"name": "Diagram Chasing",
"url": "https://diagramchasing.fun/",
"description": "A publication of data visualization and data journalism based in India.",
"country": "IN",
"framework": "sveltekit",
"categories": \["media"],
"links": \[
{
"type": "repository",
"url": "https://github.com/diagram-chasing/site"
},
{
"type": "blog-post",
"url": "https://aman.bh/blog/2025/sveltia-cms-is-golden"
}
]
},
{
"name": "Fachschaft Informatik",
"url": "https://fscs.hhu.de/de/",
"description": "Student council website for computer science students at Heinrich Heine University.",
"country": "DE",
"framework": "hugo",
"categories": \["community"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/fscs/website-theme"
}
]
},
{
"name": "Palestine: Spaces and Politics",
"url": "https://palestine.araburbanism.com/",
"description": "A research project on urbanism and politics in Palestine.",
"framework": "hugo",
"categories": \["education"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Arab-Urbanism/palurban"
}
]
},
{
"name": "Jack van Gestel",
"url": "https://jackvangestel.nl/",
"description": "Portfolio website of Jack van Gestel, a Dutch singer.",
"country": "NL",
"framework": "hugo",
"categories": \["arts", "personal"],
"links": \[
{
"type": "repository",
"url": "https://github.com/MadebyMonkeys/jackvangestel.nl"
}
]
},
{
"name": "Monierate",
"url": "https://monierate.com/",
"description": "A currency aggregator for Africa.",
"country": "NG",
"framework": "sveltekit",
"categories": \["reference"],
"links": \[
{
"type": "repository",
"url": "https://github.com/monierate/monierate-website"
}
]
},
{
"name": "Berkeley Math Tournament",
"url": "https://berkeley.mt/",
"description": "The world’s largest student-run mathematical competition.",
"country": "US",
"framework": "zola",
"categories": \["education", "community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/berkeleymt/berkeley.mt"
}
]
},
{
"name": "NC Dance",
"url": "https://ncdance.ca/",
"description": "A Latin dance studio based in Canada.",
"country": "CA",
"framework": "astro",
"categories": \["business", "arts"],
"links": \[
{
"type": "repository",
"url": "https://github.com/eventistca/site-ncdance.ca"
}
]
},
{
"name": "Janae D. Coraggio",
"url": "https://janaecoraggio.art/",
"description": "Portfolio website of Janae D. Coraggio, a children’s book illustrator.",
"country": "US",
"framework": "jekyll",
"categories": \["arts", "personal"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Chris-Coraggio/janae-coraggio-dot-art"
}
]
},
{
"name": "Accessible Canada — Accessible World",
"url": "https://acaw-cama.idrc.ocadu.ca/en/",
"description": "A 2-day conference on accessibility and inclusive design.",
"country": "CA",
"framework": "eleventy",
"categories": \["education", "event"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/inclusive-design/acaw-cama"
}
]
},
{
"name": "NTU Hall 3",
"url": "https://hall3.netlify.app/",
"description": "Website for Hall 3, a student residence at Nanyang Technology University, Singapore.",
"country": "SG",
"framework": "hugo",
"categories": \["community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Zackyization/ntu-hall-3-official-website"
}
]
},
{
"name": "A’PEL Prevention",
"url": "https://www.apelprevention.fr/",
"description": "A French company specializing in fire safety and accessibility of public buildings.",
"country": "FR",
"framework": "hugo",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/apel-prevention/apel-prevention.github.io"
}
]
},
{
"name": "Zepp OS Screen Reader",
"url": "https://www.zeppreader.com/",
"description": "Website for screen reader software developed for Amazfit smartwatches.",
"country": "VN",
"framework": "sveltekit",
"categories": \["science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/NVCDevelopmentTeam/Zepp-OS-ScreenReader"
}
]
},
{
"name": "Multiple Personality System Wiki",
"url": "https://wiki.mpsteam.cn/",
"description": "A Chinese collaborative wiki about the multiple personality system and mental health topics.",
"country": "CN",
"framework": "mkdocs",
"categories": \["documentation", "education"],
"links": \[
{
"type": "repository",
"url": "https://github.com/mps-team-cn/Multiple\_personality\_system\_wiki"
}
]
},
{
"name": "Inclusive Standards",
"url": "https://standards.inclusivedesign.ca/",
"description": "A resource providing guidelines and best practices for inclusive design standards.",
"country": "CA",
"framework": "eleventy",
"categories": \["education", "reference"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/inclusive-design/standards.inclusivedesign.ca"
}
]
},
{
"name": "Zdravíčko",
"url": "https://zdravicko.org/",
"description": "A pediatric clinic based in the Czech Republic.",
"country": "CZ",
"framework": "astro",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/janpodmolik/zdravicko"
}
]
},
{
"name": "AxOS",
"url": "https://www.axos-project.com/",
"description": "A Linux distribution focused on user experience and privacy.",
"country": "FR",
"framework": "astro",
"categories": \["science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/AxOS-project/axos-project.github.io"
}
]
},
{
"name": "Web VideoMark",
"url": "https://videomark.webdino.org/ja",
"description": "Website for a browser extension for video streaming quality assessment.",
"country": "JP",
"framework": "sveltekit",
"migratedFrom": "netlify",
"categories": \["science"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/videomark/videomark.webdino.org"
}
]
},
{
"name": "Oysterdale Records",
"url": "https://oysterdalerecords.com/",
"description": "An independent record label based in Oslo, Norway.",
"country": "NO",
"categories": \["arts", "business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Oysterdale/oysterdale-site"
}
]
},
{
"name": "Human Connection",
"url": "https://www.humanc.nl/",
"description": "A management consultancy based in the Netherlands.",
"country": "NL",
"framework": "jekyll",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/DaanBoertien/Human-Connection"
}
]
},
{
"name": "Alexandra Hedin",
"url": "https://alexandrahedin.se/",
"description": "Portfolio website of Alexandra Hedin, a Swedish opera singer.",
"country": "SE",
"framework": "astro",
"categories": \["arts", "personal"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/alexandrahedin/alexandrahedin.github.io"
}
]
},
{
"name": "Giveback Guide",
"url": "https://giveback.guide/",
"description": "A travel-focused platform promoting responsible tourism and giving back to local communities.",
"country": "GB",
"framework": "astro",
"categories": \["media"],
"links": \[
{
"type": "repository",
"url": "https://github.com/MattFM/giveback-guide"
}
]
},
{
"name": "Riverside Roofing Materials",
"url": "https://riversideroofingmaterials.com/",
"description": "A roofing materials supplier based in Louisiana, U.S.",
"country": "US",
"framework": "astro",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Bezalelab/riverside"
}
]
},
{
"name": "Alexandra Barbu",
"url": "https://alexandrabarbu.ro/",
"description": "A psychotherapist based in Romania.",
"country": "RO",
"framework": "hugo",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/cpantus/alexandrabarbu.ro"
}
]
},
{
"name": "Guidebook for Financial Inclusion",
"url": "https://financial-inclusion.inclusivedesign.ca/",
"description": "A guide for financial institutions to understand barriers and create more inclusive financial offerings.",
"country": "CA",
"framework": "eleventy",
"categories": \["education", "reference"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/inclusive-design/financial-inclusion"
}
]
},
{
"name": "Mundo Dolphins Podcast",
"url": "https://mundodolphins.es/",
"description": "Spanish-language podcast dedicated to the Miami Dolphins NFL team.",
"country": "US",
"framework": "hugo",
"categories": \["media", "sports"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Mundo-Dolphins/mundo-dolphins.github.io"
}
]
},
{
"name": "Centre for Networked Intelligence",
"url": "https://cni.iisc.ac.in/",
"description": "A research center at the Indian Institute of Science focused on next-generation networking technologies.",
"country": "IN",
"framework": "eleventy",
"categories": \["education", "science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/cni-iisc/cni\_11ty"
}
]
},
{
"name": "Weavly Co-Design",
"url": "https://co-design.weavly.org/",
"description": "A resource for co-designing with children with disabilities.",
"country": "CA",
"framework": "eleventy",
"categories": \["education"],
"links": \[
{
"type": "repository",
"url": "https://github.com/codelearncreate/co-design"
}
]
},
{
"name": "Novala Biotech",
"url": "https://www.novala.com.np/",
"description": "A biotechnology company based in Nepal.",
"country": "NP",
"framework": "hugo",
"categories": \["business", "science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/novalabiotech/novalabiotech.github.io"
}
]
},
{
"name": "IGDA Game Writing SIG",
"url": "https://www.game-writing.com/",
"country": "US",
"description": "The Game Writing Special Interest Group of the International Game Developers Association.",
"framework": "astro",
"categories": \["community", "arts"],
"links": \[
{
"type": "repository",
"url": "https://github.com/gwsig-tech/game-writing.com"
}
]
},
{
"name": "EPRN",
"url": "https://eprn.fr/",
"description": "A French company specializing in pest control and environmental services.",
"country": "FR",
"framework": "jekyll",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/TheDoubleJo/eprn"
}
]
},
{
"name": "RCTV19",
"url": "https://rctv19.com/",
"description": "A community television station based in Mississippi, U.S.",
"country": "US",
"framework": "eleventy",
"categories": \["media"],
"links": \[
{
"type": "repository",
"url": "https://github.com/wallyrebel/rctv19"
}
]
},
{
"name": "UV",
"url": "https://www.uv.agency/",
"description": "A marketing agency headquartered in Santiago, Chile.",
"country": "CL",
"framework": "react-router",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/UVagency/uv2025"
}
]
},
{
"name": "Abadicomm",
"url": "https://abadicomm.id/",
"description": "An event management company based in Indonesia.",
"country": "ID",
"framework": "astro",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/alizaenazet/abadicomm\_sitev2"
}
]
},
{
"name": "SleakOps Documentation",
"url": "https://docs.sleakops.com/",
"description": "Documentation site for a cloud automation platform.",
"country": "AR",
"framework": "docusaurus",
"categories": \["science", "documentation"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/sleakops/docs"
}
]
},
{
"name": "Saara El-Arifi",
"url": "https://saaraelarifi.com/",
"description": "Website of Saara El-Arifi, a British fantasy author.",
"country": "GB",
"migratedFrom": "wix",
"categories": \["arts", "personal"],
"links": \[
{
"type": "repository",
"url": "https://github.com/jimdynasty/SEAweb"
}
]
},
{
"name": "Spectrum Lab",
"url": "https://spectrum.ee.iisc.ac.in/",
"description": "A research group at the Indian Institute of Science focused on computational imaging and machine learning.",
"country": "IN",
"framework": "jekyll",
"categories": \["education", "science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/spectrum-lab-iisc/spectrum-lab-iisc.github.io"
}
]
},
{
"name": "Elzara Oiseau",
"url": "https://elzara.studio/",
"description": "Portfolio website of Elzara Oiseau, a visual artist based in Switzerland.",
"country": "CH",
"framework": "next",
"categories": \["arts", "personal"],
"links": \[
{
"type": "repository",
"url": "https://github.com/martpie/elzara"
}
]
},
{
"name": "Decidim",
"url": "https://decidim.org/",
"description": "The official website of Decidim, an open-source participatory democracy platform.",
"country": "ES",
"framework": "middleman",
"categories": \["science", "community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/decidim/decidim.org"
}
]
},
{
"name": "Buckley & Sitzman",
"url": "https://www.buckleysitzman.com/",
"description": "A public accounting firm based in Nebraska, U.S.",
"country": "US",
"framework": "eleventy",
"migratedFrom": "netlify",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Buckley-Sitzman-LLP/buckley-sitzman-com"
}
]
},
{
"name": "Pura Verdura",
"url": "https://www.puraverdura.ch/",
"description": "A solidarity cooperative in Zurich that grows organic vegetables locally, ecologically, and socially.",
"country": "CH",
"framework": "sveltekit",
"categories": \["community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/puraverdura/website"
}
]
},
{
"name": "Pike & West",
"url": "https://pikeandwest.com/",
"description": "An art gallery and event venue based in Tennessee, U.S.",
"country": "US",
"framework": "hugo",
"categories": \["arts", "business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/cneller/pikeandwest.com"
}
]
},
{
"name": "Bringmal Changelog",
"url": "https://changelog.bringmal.app/",
"description": "Changelog website for Bringmal, a SaaS platform for restaurants.",
"country": "DE",
"framework": "astro",
"categories": \["science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/kevineulenberg/bringmal-changelog"
}
]
},
{
"name": "Harsh Shandilya",
"url": "https://msfjarvis.dev/",
"description": "Personal website of Harsh Shandilya, an Android developer and prolific open source contributor.",
"country": "IN",
"framework": "hugo",
"categories": \["personal", "science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/msfjarvis/msfjarvis.dev"
},
{
"type": "blog-post",
"url": "https://msfjarvis.dev/notes/welcome-to-notes/"
}
]
},
{
"name": "MigAct",
"url": "https://migact.net/",
"description": "A Czech organization promoting inclusion and active citizenship.",
"country": "CZ",
"framework": "hugo",
"migratedFrom": "wordpress",
"categories": \["community"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/MigActCZ/website"
}
]
},
{
"name": "Veronika Rotfuß",
"url": "https://veronikarotfuss.de/",
"description": "A psychotherapist based in Munich.",
"country": "DE",
"framework": "jekyll",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/vrotfuss/website"
}
]
},
{
"name": "Saddle River Deli",
"url": "https://saddleriverdeli.com/",
"description": "A deli restaurant based in New Jersey, U.S.",
"country": "US",
"framework": "astro",
"categories": \["retail"],
"links": \[
{
"type": "repository",
"url": "https://github.com/sellas369/saddle-river-deli"
}
]
},
{
"name": "SS Thistlegorm",
"url": "https://ssthistlegorm.com/",
"description": "A website dedicated to the SS Thistlegorm, a British merchant ship sunk during World War II.",
"country": "GB",
"framework": "jekyll",
"categories": \["education"],
"links": \[
{
"type": "repository",
"url": "https://github.com/lolandese1/ssthistlegorm.github.io"
}
]
},
{
"name": "Plant a Seed",
"url": "https://www.plantaseed.store/",
"description": "An online store based in Mauritius, selling plants and planters.",
"country": "MU",
"framework": "react-router",
"categories": \["retail"],
"links": \[
{
"type": "repository",
"url": "https://github.com/divyashie/Plantaseedwebapp"
}
]
},
{
"name": "Anne Sophie Grac",
"url": "https://www.annesophiegrac.com/",
"description": "Portfolio website of Anne Sophie Grac, a French set designer and costume designer.",
"country": "FR",
"framework": "sveltekit",
"categories": \["arts", "personal"],
"links": \[
{
"type": "repository",
"url": "https://github.com/mathieugrac/portfolio-so"
}
]
},
{
"name": "Corfu Literary Festival",
"url": "https://www.corfuliteraryfestival.com/",
"description": "An annual literary festival held on the island of Corfu, Greece.",
"country": "GR",
"framework": "eleventy",
"categories": \["arts", "event"],
"links": \[
{
"type": "repository",
"url": "https://gitlab.com/sebduggan/corfu-literary-festival"
}
]
},
{
"name": "Alex Preston",
"url": "https://alexhmpreston.com/",
"description": "Personal website of Alex Preston, a British author and journalist.",
"country": "GB",
"framework": "eleventy",
"categories": \["arts", "personal"],
"links": \[
{
"type": "repository",
"url": "https://gitlab.com/sebduggan/alex-preston"
}
]
},
{
"name": "Malmö BMX Racing",
"url": "https://malmobmxracing.se/",
"description": "A BMX racing club based in Malmö, Sweden.",
"country": "SE",
"framework": "next",
"migratedFrom": "netlify",
"categories": \["community", "sports"],
"links": \[
{
"type": "repository",
"url": "https://github.com/andersjalevik/malmo-bmx-racing"
}
]
},
{
"name": "Cascadia AI Collective",
"url": "https://www.cascadiaaiwomen.tech/",
"description": "A community connecting women+ in AI across the Pacific Northwest.",
"country": "US",
"framework": "eleventy",
"categories": \["community", "science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Yosolita1978/cascadiaaicollective"
}
]
},
{
"name": "Skelton Gate Community",
"url": "https://skeltongate.homes/",
"description": "A local community hub for the Skelton Gate area near Leeds, UK.",
"country": "GB",
"framework": "hugo",
"categories": \["community", "reference"],
"links": \[
{
"type": "repository",
"url": "https://github.com/Raithmir/skelton"
}
]
},
{
"name": "Visioning Lab",
"url": "https://visioninglab.com/",
"description": "A creative digital studio based in Manchester, UK.",
"country": "GB",
"framework": "next",
"categories": \["science", "business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/visioninglab/website"
}
]
},
{
"name": "Bambino Therapy",
"url": "https://bambinotherapy.com/",
"description": "A speech therapy clinic in Bengaluru, India.",
"country": "IN",
"framework": "eleventy",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/MarioWindsor/bamnio-website"
}
]
},
{
"name": "Tupelo HVAC",
"url": "https://tupelohvac.com/",
"description": "A service platform connecting Tupelo area homeowners with HVAC professionals.",
"country": "US",
"framework": "astro",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/wallyrebel/tupelohvac"
}
]
},
{
"name": "Royale Builders & Developers",
"url": "https://royalebuilders.com/",
"description": "A luxury real estate developer based in Bengaluru, India.",
"country": "IN",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/sidharthb0/royalebuilders"
}
]
},
{
"name": "HairVits",
"url": "https://hairvits.com/",
"description": "Expert hair supplement reviews and evidence-based beauty and wellness insights.",
"country": "GB",
"framework": "vitepress",
"categories": \["media", "science"],
"links": \[
{
"type": "repository",
"url": "https://github.com/hairburst/hairvits"
}
]
},
{
"name": "Mage-OS",
"url": "https://mage-os.org/",
"description": "Website for an open-source ecommerce platform built on Magento Open Source.",
"country": "PL",
"framework": "astro",
"migratedFrom": "wordpress",
"categories": \["science", "community"],
"links": \[
{
"type": "repository",
"url": "https://github.com/mage-os/mage-os.org"
}
]
},
{
"name": "Niels-Jonas Simons",
"url": "https://njonas.de/",
"description": "Portfolio website of Niels-Jonas Simons, a cameraman, video editor and multimedia designer based in Cologne.",
"country": "DE",
"framework": "jekyll",
"migratedFrom": "decap",
"categories": \["personal", "arts"],
"links": \[
{
"type": "repository",
"url": "https://github.com/janschill/njonassimons.de"
}
]
},
{
"name": "Privat Eden",
"url": "https://privateden.sk/",
"description": "A family guesthouse in the Liptov region of Slovakia.",
"country": "SK",
"framework": "astro",
"categories": \["business"],
"features": \["i18n"],
"links": \[
{
"type": "repository",
"url": "https://github.com/zbynekdrlik/privateden-website"
}
]
},
{
"name": "Absolutely Floorless",
"url": "https://absolutely-floorless.co.uk/",
"description": "A carpet cleaning company based in the UK.",
"country": "GB",
"framework": "astro",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/tomkingswood/absolutelyfloorless"
}
]
},
{
"name": "Ecofinconseils",
"url": "https://ecofinconseils.com/",
"description": "A financial consulting firm based in Bamako, Mali.",
"country": "ML",
"framework": "astro",
"categories": \["business"],
"links": \[
{
"type": "repository",
"url": "https://github.com/KATBlackCoder/Ecofinconseils"
}
]
}
]
*Note: The migration and feature data is incomplete. We are working on gathering more information and will update the showcase regularly.*
More projects, including small personal blogs and portfolios, can be found by [searching GitHub](https://github.com/search?q=sveltia\&type=commits\&s=committer-date\&o=desc). Note that the code search only returns 100 results from public repositories, so there are many more out there!
---
---
url: /en/docs/workflows/simple.md
description: >-
Implement a simple workflow in Sveltia CMS for direct content editing without
review.
---
# Simple Workflow
This is the default remote workflow suitable for single users or small projects. There would be no review process, and changes are made directly to the repository.
## Use Cases
* Individual bloggers or content creators managing their own websites.
* Small teams or projects where a formal review process is unnecessary.
* Quick content updates or changes that do not require oversight.
## Requirements
No special requirements are needed to use the simple workflow. Users can start making changes directly after setting up their Sveltia CMS instance.
## Configuration
No specific configuration is required for this workflow.
## Workflow
The simple workflow allows users to create, edit, and delete entries directly in the connected Git repository without any review process. Here’s how it works:
1. Log in to Sveltia CMS using the standard OAuth authentication process or your access token.
2. Navigate to the desired collection from the collection list.
3. Create, edit, or delete entries as needed.
4. Save your changes. Sveltia CMS will automatically commit and push the changes to the connected Git repository.
## Deploying Changes
Changes made through Sveltia CMS are automatically committed and pushed to the connected repository’s default branch (e.g., `main` or `master`, unless the `branch` option is set). If you have set up CI/CD for your site, the changes will be deployed automatically based on your existing deployment process.
See the [deployments guide](/en/docs/deployments) for more details, including how to disable automatic deployments if needed.
## Multiple Editors
While this workflow is designed for single users, multiple editors can still collaborate by coordinating their changes. However, since there is no review process, it is essential to communicate effectively to avoid conflicts and ensure that everyone is aware of the changes being made.
At this time, Sveltia CMS does not provide built-in features for handling merge conflicts or simultaneous edits. We plan to add such features in future releases to enhance collaboration in the simple workflow.
---
---
url: /en/docs/collections/singletons.md
description: >-
Use singleton collections in Sveltia CMS to manage pre-defined data files and
unique resources.
---
# Singletons
The Singleton collection is a special type of file collection that allows you to manage a set of pre-defined data files, each representing a unique resource in your project. Unlike regular file collections, the Singleton collection does not have a collection name or label, and each file is defined directly at the root level of the configuration.
::: tip
Singletons may be referred to as “singles” or “singular resources” in other CMSs.
:::
## Differences from File Collections
The differences between the Singleton collection and a regular [file collection](/en/docs/collections/files) are as follows:
### Configuration
* The Singleton collection does not have the `name` or `label` property at the root level of the configuration.
* Each file in the Singleton collection is defined directly under the `singletons` array at the root level of the configuration, rather than being nested under a `files` property within a named collection.
* Singleton files cannot be nested within folders; each file must be defined at the top level of the `singletons` array.
### User Interface
* On desktop, singleton files appear directly in the sidebar under the “Singletons” group, rather than within a collection that shows a list of files.
* When clicking on a singleton file in the sidebar, the editor opens directly for that file.
* However, if there are no other collections, the Singleton collection appears as a regular file collection.
* On mobile, singleton files are accessible via a dedicated “Singletons” section in the content library.
## When to Use Singletons
If your project has multiple similar files, you might consider creating a regular [file collection](/en/docs/collections/files) and include all your relevant files there. A typical example is a `pages` collection that contains multiple page files like `home`, `about`, and `contact`.
However, if your project has only a few pages or configuration files that are not part of a larger collection, using singletons can be more straightforward.
For example, you might have a `home` page and a `settings` file that you want to manage. Instead of creating a `pages` collection with just one file, you can define these files directly in the Singleton collection.
## Creating the Singleton Collection
To create this special file collection, add the new `singletons` option, along with an array of file definitions, to the root level of your CMS configuration.
Here’s an example configuration with two singleton files:
::: code-group
```yaml [YAML]
singletons:
- name: home
label: Home Page
file: content/home.yaml
fields:
- { label: Title, name: title }
- { label: Body, name: body, widget: richtext }
- name: settings
label: Site Settings
file: content/settings.yaml
fields:
- { label: Site Title, name: site_title }
- { label: Description, name: description, widget: text }
```
```toml [TOML]
[[singletons]]
name = "home"
label = "Home Page"
file = "content/home.yaml"
[[singletons.fields]]
label = "Title"
name = "title"
[[singletons.fields]]
label = "Body"
name = "body"
widget = "richtext"
[[singletons]]
name = "settings"
label = "Site Settings"
file = "content/settings.yaml"
[[singletons.fields]]
label = "Site Title"
name = "site_title"
[[singletons.fields]]
label = "Description"
name = "description"
widget = "text"
```
```json [JSON]
{
"singletons": [
{
"name": "home",
"label": "Home Page",
"file": "content/home.yaml",
"fields": [
{ "label": "Title", "name": "title" },
{ "label": "Body", "name": "body", "widget": "richtext" }
]
},
{
"name": "settings",
"label": "Site Settings",
"file": "content/settings.yaml",
"fields": [
{ "label": "Site Title", "name": "site_title" },
{ "label": "Description", "name": "description", "widget": "text" }
]
}
]
}
```
```js [JavaScript]
{
singletons: [
{
name: "home",
label: "Home Page",
file: "content/home.yaml",
fields: [
{ label: "Title", name: "title" },
{ label: "Body", name: "body", widget: "richtext" },
],
},
{
name: "settings",
label: "Site Settings",
file: "content/settings.yaml",
fields: [
{ label: "Site Title", name: "site_title" },
{ label: "Description", name: "description", widget: "text" },
],
},
],
}
```
:::
File options are the same as those for [file collections](/en/docs/collections/files).
## Converting from File Collections
It’s easy to convert an existing file collection into the Singleton collection. This is a conventional file collection:
::: code-group
```yaml [YAML]{1-4}
collections:
- name: data
label: Data
files:
- name: home
label: Home Page
file: content/home.yaml
fields: ...
- name: settings
label: Site Settings
file: content/settings.yaml
fields: ...
```
```toml [TOML]{1-3}
[[collections]]
name = "data"
label = "Data"
[[collections.files]]
name = "home"
label = "Home Page"
file = "content/home.yaml"
[[collections.files]]
name = "settings"
label = "Site Settings"
file = "content/settings.yaml"
```
```json [JSON]{2-6}
{
"collections": [
{
"name": "data",
"label": "Data",
"files": [
{
"name": "home",
"label": "Home Page",
"file": "content/home.yaml"
},
{
"name": "settings",
"label": "Site Settings",
"file": "content/settings.yaml"
}
]
}
]
}
```
```js [JavaScript]{2-6}
{
collections: [
{
name: "data",
label: "Data",
files: [
{
name: "home",
label: "Home Page",
file: "content/home.yaml",
},
{
name: "settings",
label: "Site Settings",
file: "content/settings.yaml",
},
],
},
],
}
```
:::
It can be converted to the Singleton collection like this:
::: code-group
```yaml [YAML]{1}
singletons:
- name: home
label: Home Page
file: content/home.yaml
fields: ...
- name: settings
label: Site Settings
file: content/settings.yaml
fields: ...
```
```toml [TOML]
[[singletons]]
name = "home"
label = "Home Page"
file = "content/home.yaml"
[[singletons]]
name = "settings"
label = "Site Settings"
file = "content/settings.yaml"
```
```json [JSON]{2}
{
"singletons": [
{
"name": "home",
"label": "Home Page",
"file": "content/home.yaml"
},
{
"name": "settings",
"label": "Site Settings",
"file": "content/settings.yaml"
}
]
}
```
```js [JavaScript]{2}
{
singletons: [
{
name: "home",
label: "Home Page",
file: "content/home.yaml",
},
{
name: "settings",
label: "Site Settings",
file: "content/settings.yaml",
},
],
}
```
:::
## Adding Icons and Dividers
You can add icons to singleton items using the `icon` option, and you can add dividers between items using the `divider` option. Here’s an example:
::: code-group
```yaml [YAML]{5,7,11}
singletons:
- name: home
label: Home Page
file: content/home.yaml
icon: home
fields: ...
- divider: true
- name: settings
label: Site Settings
file: content/settings.yaml
icon: settings
fields: ...
```
```toml [TOML]{5,8,14}
[[singletons]]
name = "home"
label = "Home Page"
file = "content/home.yaml"
icon = "home"
[[singletons]]
divider = true
[[singletons]]
name = "settings"
label = "Site Settings"
file = "content/settings.yaml"
icon = "settings"
```
```json [JSON]{7,10,16}
{
"singletons": [
{
"name": "home",
"label": "Home Page",
"file": "content/home.yaml",
"icon": "home"
},
{
"divider": true
},
{
"name": "settings",
"label": "Site Settings",
"file": "content/settings.yaml",
"icon": "settings"
}
]
}
```
```js [JavaScript]{7,10,16}
{
singletons: [
{
name: "home",
label: "Home Page",
file: "content/home.yaml",
icon: "home",
},
{
divider: true,
},
{
name: "settings",
label: "Site Settings",
file: "content/settings.yaml",
icon: "settings",
},
],
}
```
:::
## Referencing Singleton Files
If you want to reference a singleton file with a [Relation](/en/docs/fields/relation) field, use `_singletons` (note an underscore prefix) as the `collection` name.
---
---
url: /en/docs/integrations/stock-photos.md
description: >-
Search and insert free stock photos from popular providers directly in Sveltia
CMS.
---
# Stock Photos
Sveltia CMS includes built-in support for integrating free stock photo services directly into the media storage. This allows content creators to easily search for and insert high-quality images into their content without leaving the CMS interface.
## Supported Services
Currently, Sveltia CMS supports the following free stock photo providers:
* [Pexels](https://www.pexels.com/)
* [Pixabay](https://pixabay.com/)
* [Unsplash](https://unsplash.com/)
::: info Future Plans
More providers, including paid stock photo services, will be added in future releases.
:::
## Requirements
You must obtain API keys for each stock photo service you wish to use. Register for developer accounts at the following links to get your API keys:
* [Pexels API](https://www.pexels.com/api/)
* [Pixabay API](https://pixabay.com/service/about/api/)
* [Unsplash API](https://unsplash.com/developers)
::: tip CSP Settings
If your site uses a Content Security Policy (CSP), you may need to update it to allow requests to stock photo providers. See the [CSP documentation](/en/docs/security#setting-up-content-security-policy) for more details.
:::
## Configuration
No additional configuration is required to enable stock photo providers, as they are included by default. However, developers can customize which providers are available or disable them entirely.
To enable or disable specific stock photo providers, you can modify the `providers` array in the `media_libraries` section of your `config.yml` file. For example, to enable only Unsplash, you would configure it as follows:
::: code-group
```yaml [YAML]
media_libraries:
stock_assets:
providers:
- unsplash
```
```toml [TOML]
[media_libraries.stock_assets]
providers = ["unsplash"]
```
```json [JSON]
{
"media_libraries": {
"stock_assets": {
"providers": ["unsplash"]
}
}
}
```
```js [JavaScript]
{
media_libraries: {
stock_assets: {
providers: ["unsplash"],
},
},
}
```
:::
To disable stock photo providers, you can set the `providers` array to be empty in the `media_libraries` section of your `config.yml` file, as shown below:
::: code-group
```yaml [YAML]
media_libraries:
stock_assets:
providers: []
```
```toml [TOML]
[media_libraries.stock_assets]
providers = []
```
```json [JSON]
{
"media_libraries": {
"stock_assets": {
"providers": []
}
}
}
```
```js [JavaScript]
{
media_libraries: {
stock_assets: {
providers: [],
},
},
}
```
:::
## Using Stock Photos
Stock photos can be accessed directly from the [Image](/en/docs/fields/image) field in the Sveltia CMS interface. When opening the Select Images dialog, you will see stock photo options alongside your existing media storage. Select the desired stock photo provider and provide your API key to enable the service. API keys are stored securely in the browser’s local storage, so you don’t need to enter them every time.
By default, curated images from each provider are available for quick access. You can also use the search functionality to find specific images based on keywords.
When you find an image you want to use, simply select it, and it will be inserted into your content just like any other media asset. The CMS may show a dialog where you can copy the image credit information to comply with attribution requirements.
Note that some providers require hotlinking to the image’s original URL, while others rather disallow hotlinking. Sveltia CMS handles this automatically based on the provider’s policies, and downloads the image to your media storage if necessary.
::: tip Multi-user setups
If your CMS instance is used by multiple users, you as an administrator need to distribute your API key to all users to let them use the stock photo feature.
Alternatively, each user can provide their own API key. However, this is not recommended for instances with non-technical end-users because generating and managing API keys may be challenging for them.
:::
::: info Limitations
Due to the non-hotlinking policies, some stock photo providers are disabled if the [internal media storage](/en/docs/media/internal) is disabled due to the lack of the [`media_folder` option](/en/docs/media/internal#media-folder) in the configuration.
:::
---
---
url: /en/docs/fields/string.md
description: Enter and manage short text values in Sveltia CMS for content entries.
---
# String Field
The String field type allows users to input and manage short to medium-length text strings within the CMS entry form.
::: tip Alternative for longer or multiple strings
If you need to handle longer text content, consider using the [Text](/en/docs/fields/text) or [RichText](/en/docs/fields/richtext) field type instead.
If you need to manage multiple strings, consider using the simple [List](/en/docs/fields/list) field type instead.
:::
## User Interface
### Editor
Single-line text input field for entering short to medium-length strings. It supports standard text input features like copy-paste, undo-redo, and basic keyboard shortcuts.
Additional text can be displayed before or after the input field using the `before_input` and `after_input` options.
A character counter can be displayed if `minlength` or `maxlength` option is set, and a user-friendly validation message will appear if the input does not meet the specified length requirements.
### Preview
A read-only view of the entered string. If the `prefix` or `suffix` options are set, they will be displayed along with the string in the preview.
If the string is a YouTube video URL, it will be automatically embedded in the preview for better visualization.
If the string is a regular URL, it will be displayed as a clickable link that opens in a new browser tab.
::: tip CSP Settings
You may need to update your Content Security Policy (CSP) to allow embedding YouTube videos. See the [CSP documentation](/en/docs/security#setting-up-content-security-policy) for more details.
:::
## Data Type
A string. If the `required` option is set to `false` and the field is left empty, the value will be an empty string.
## Data Validation
* If the `required` option is set to `true`, the string must not be empty.
* If `minlength` and/or `maxlength` options are specified, the string length must be within the defined limits.
* If the `pattern` option is provided, the string must match the specified regular expression pattern.
## Options
In addition to the [common field options](/en/docs/fields#common-options), the String field supports the following options:
### Optional Options
#### `widget`
* **Type**: `string`
* **Default**: `string`
Must be set to `string`, but is optional since it is the default field type.
#### `default`
* Type: `string`
* **Default**: `""`
The default value for the field when creating a new entry.
#### `type`
* Type: `string`
* **Default**: `""`
The value type, either `url` or `email`. This option changes the input type attribute accordingly for better mobile keyboard support, and also enables basic validation for URL or email format.
#### `prefix`
* **Type**: `string`
* **Default**: `""`
Strings to be prepended to the value when saving or displaying it. If the value is empty, the prefix will not be added.
#### `suffix`
* **Type**: `string`
* **Default**: `""`
Strings to be appended to the value when saving or displaying it. If the value is empty, the suffix will not be added.
#### `minlength`
* **Type**: `integer`
* **Default**: `0`
Minimum length of the string. This enables character counter in the UI and validation.
#### `maxlength`
* **Type**: `integer`
* **Default**: `Infinity`
Maximum length of the string. This enables character counter in the UI and validation.
#### `before_input`
* **Type**: `string`
* **Default**: `""`
Text to display before the input field.
#### `after_input`
* **Type**: `string`
* **Default**: `""`
Text to display after the input field.
## Examples
### Basic String Field
The following example demonstrates a basic String field for entering a title. Note that the `widget` option is optional since `string` is the default field type.
::: code-group
```yaml [YAML]
- name: title
label: Title
```
```toml [TOML]
[[fields]]
name = "title"
label = "Title"
```
```json [JSON]
{
"name": "title",
"label": "Title"
}
```
```js [JavaScript]
{
name: "title",
label: "Title",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
title: My First Post
```
```toml [TOML]
title = "My First Post"
```
```json [JSON]
{
"title": "My First Post"
}
```
:::
### Minimum and Maximum Length
The following example demonstrates a String field with `minlength` and `maxlength` options set to enforce input length constraints. It also includes a `default` value.
::: code-group
```yaml [YAML]
- name: title
label: Title
widget: string
default: 'Enter your title here.'
minlength: 5
maxlength: 100
```
```toml [TOML]
[[fields]]
name = "title"
label = "Title"
widget = "string"
default = "Enter your title here."
minlength = 5
maxlength = 100
```
```json [JSON]
{
"name": "title",
"label": "Title",
"widget": "string",
"default": "Enter your title here.",
"minlength": 5,
"maxlength": 100
}
```
```js [JavaScript]
{
name: "title",
label: "Title",
widget: "string",
default: "Enter your title here.",
minlength: 5,
maxlength: 100,
}
```
:::
Output example:
::: code-group
```yaml [YAML]
title: My Second Post
```
```toml [TOML]
title = "My Second Post"
```
```json [JSON]
{
"title": "My Second Post"
}
```
:::
### URL Field
The following example demonstrates a String field configured for URL input. Validation will ensure that the entered value is a properly formatted URL.
::: code-group
```yaml [YAML]
- name: website
label: Website
widget: string
type: url
```
```toml [TOML]
[[fields]]
name = "website"
label = "Website"
widget = "string"
type = "url"
```
```json [JSON]
{
"name": "website",
"label": "Website",
"widget": "string",
"type": "url"
}
```
```js [JavaScript]
{
name: "website",
label: "Website",
widget: "string",
type: "url",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
website: https://example.com
```
```toml [TOML]
website = "https://example.com"
```
```json [JSON]
{
"website": "https://example.com"
}
```
:::
### Email Field with Prefix and Suffix
Some use cases may require adding specific text before or after the input value, such as `mailto:` for email links or query parameters. The following example demonstrates a String field configured for email input with `prefix` and `suffix` options.
::: code-group
```yaml [YAML]
- name: contact_email_link
label: Contact Email Link
widget: string
type: email
prefix: 'mailto:'
suffix: '?subject=Inquiry'
```
```toml [TOML]
[[fields]]
name = "contact_email_link"
label = "Contact Email Link"
widget = "string"
type = "email"
prefix = "mailto:"
suffix = "?subject=Inquiry"
```
```json [JSON]
{
"name": "contact_email_link",
"label": "Contact Email Link",
"widget": "string",
"type": "email",
"prefix": "mailto:",
"suffix": "?subject=Inquiry"
}
```
```js [JavaScript]
{
name: "contact_email_link",
label: "Contact Email Link",
widget: "string",
type: "email",
prefix: "mailto:",
suffix: "?subject=Inquiry",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
contact_email_link: mailto:contact@example.com?subject=Inquiry
```
```toml [TOML]
contact_email_link = "mailto:contact@example.com?subject=Inquiry"
```
```json [JSON]
{
"contact_email_link": "mailto:contact@example.com?subject=Inquiry"
}
```
```js [JavaScript]
{
contact_email_link: 'mailto:contact@example.com?subject=Inquiry';
}
```
:::
Alternatively, you can use a Compute field to generate the full email link based on a separate email String field, as shown in the [Compute field documentation](/en/docs/fields/compute#email-link).
### Hashtag Field
The following example demonstrates a String field configured for entering hashtags, with a `#` symbol displayed before the input field and a hint to guide users. Unlike the `prefix` option, which adds text to the saved value, the `before_input` option only affects the UI display, not the stored data.
::: code-group
```yaml [YAML]
- name: hashtag
label: Hashtag
widget: string
before_input: '#'
hint: 'Enter a hashtag without the # symbol.'
```
```toml [TOML]
[[fields]]
name = "hashtag"
label = "Hashtag"
widget = "string"
before_input = "#"
hint = "Enter a hashtag without the # symbol."
```
```json [JSON]
{
"name": "hashtag",
"label": "Hashtag",
"widget": "string",
"before_input": "#",
"hint": "Enter a hashtag without the # symbol."
}
```
```js [JavaScript]
{
name: "hashtag",
label: "Hashtag",
widget: "string",
before_input: "#",
hint: "Enter a hashtag without the # symbol.",
}
```
:::
Output example:
::: code-group
```yaml [YAML]
hashtag: travel
```
```toml [TOML]
hashtag = "travel"
```
```json [JSON]
{
"hashtag": "travel"
}
```
:::
---
---
url: /en/docs/string-transformations.md
description: >-
Use string transformations in Sveltia CMS to manipulate and format content
values.
---
# String Transformations
String transformations allow you to manipulate and format string values in your content entries. These transformations can be applied in various contexts, such as generating summaries or formatting dates.
::: tip Note for Netlify/Decap CMS users
In Netlify/Decap CMS, this feature is known as **summary string transformations**. We simply refer to them as **string transformations** because, in Sveltia CMS, they can be used in multiple contexts beyond just entry summaries.
:::
## Available Contexts
String transformations can be applied in the following contexts:
* [Entry Collection](/en/docs/collections/entries): the `summary`, `slug`, `path`, `preview_path` options
* [List Field](/en/docs/fields/list): the `summary` option
* [Object Field](/en/docs/fields/object): the `summary` option
::: info Future Plans
More contexts may be added in future releases.
:::
## Syntax
String transformations are applied using the following syntax:
```
{{value | transformation_name}}
```
or with arguments:
```
{{value | transformation_name(arguments)}}
```
Where:
* `value`: The original string value to be transformed. This is typically a field name enclosed in double curly braces (e.g., `{{title}}`).
* `transformation_name`: The name of the transformation to apply (e.g., `upper`, `lower`, `truncate`).
* `arguments`: Optional arguments required by certain transformations, enclosed in parentheses.
Multiple transformations can be chained together by separating them with a pipe (`|`). The output of one transformation serves as the input for the next. For example:
```
{{value | transformation1 | transformation2(arguments)}}
```
### Notes on Syntax Rules
There are some syntax rules to keep in mind when using string transformations:
* String arguments must be enclosed in single quotes.
* Numeric arguments should not be quoted.
* No spaces are allowed between the transformation name and the opening parenthesis.
* No spaces are allowed after the starting curly braces (`{{`) or before the ending curly braces (`}}`).
* A space is required before and after the pipe character (`|`).
We might relax some of these rules in future releases.
## Available Transformations
### `upper`
Transform the string to uppercase.
```
{{value | upper}}
```
Configuration example:
::: code-group
```yaml [YAML]
summary: '{{title | upper}}'
```
```toml [TOML]
summary = "{{title | upper}}"
```
```json [JSON]
{
"summary": "{{title | upper}}"
}
```
```js [JavaScript]
{
summary: "{{title | upper}}",
}
```
:::
### `lower`
Transform the string to lowercase.
```
{{value | lower}}
```
Configuration example:
::: code-group
```yaml [YAML]
summary: '{{title | lower}}'
```
```toml [TOML]
summary = "{{title | lower}}"
```
```json [JSON]
{
"summary": "{{title | lower}}"
}
```
```js [JavaScript]
{
summary: "{{title | lower}}",
}
```
:::
### `truncate`
Truncate the string to a specified length, optionally adding a suffix.
```
{{value | truncate()}}
```
```
{{value | truncate(, '')}}
```
The `length` argument specifies the maximum number of characters to keep. If the string exceeds this length, it will be truncated. The optional `suffix` argument allows you to specify a string to append to the truncated string (e.g., an ellipsis).
Configuration examples:
::: code-group
```yaml [YAML]
summary: '{{content | truncate(100)}}'
```
```toml [TOML]
summary = "{{content | truncate(100)}}"
```
```json [JSON]
{
"summary": "{{content | truncate(100)}}"
}
```
```js [JavaScript]
{
summary: "{{content | truncate(100)}}",
}
```
:::
::: code-group
```yaml [YAML]
summary: "{{content | truncate(100, '...')}}"
```
```toml [TOML]
summary = "{{content | truncate(100, '...')}}"
```
```json [JSON]
{
"summary": "{{content | truncate(100, '...')}}"
}
```
```js [JavaScript]
{
summary: "{{content | truncate(100, '...')}}",
}
```
:::
### `default`
Provide a default value if the original value is null or empty.
```
{{value | default('')}}
```
The `default_value` is returned if the original value is null or an empty string; otherwise, the original value is returned.
Configuration example:
::: code-group
```yaml [YAML]
summary: "{{description | default('No description available.')}}"
```
```toml [TOML]
summary = "{{description | default('No description available.')}}"
```
```json [JSON]
{
"summary": "{{description | default('No description available.')}}"
}
```
```js [JavaScript]
{
summary: "{{description | default('No description available.')}}",
}
```
:::
It’s possible to fall back to another field’s value using a nested template:
::: code-group
```yaml [YAML]
preview_path: "/{{fields.slug | default('{{fields.title}}')}}/"
```
```toml [TOML]
preview_path = "/{{fields.slug | default('{{fields.title}}')}}/"
```
```json [JSON]
{
"preview_path": "/{{fields.slug | default('{{fields.title}}')}}/"
}
```
```js [JavaScript]
{
preview_path: "/{{fields.slug | default('{{fields.title}}')}}/",
}
```
:::
### `ternary`
Choose between two values based on the truthiness of the original value.
```
{{value | ternary('', '')}}
```
The `true_value` is returned if the original value is truthy; otherwise, the `false_value` is returned.
Configuration examples:
::: code-group
```yaml [YAML]
summary: "{{is_private | ternary('Private', 'Public')}}: {{title}}"
```
```toml [TOML]
summary = "{{is_private | ternary('Private', 'Public')}}: {{title}}"
```
```json [JSON]
{
"summary": "{{is_private | ternary('Private', 'Public')}}: {{title}}"
}
```
```js [JavaScript]
{
summary: "{{is_private | ternary('Private', 'Public')}}: {{title}}",
}
```
:::
::: code-group
```yaml [YAML]
summary: "{{title}} – {{featured | ternary('Featured', 'Regular')}} Event"
```
```toml [TOML]
summary = "{{title}} – {{featured | ternary('Featured', 'Regular')}} Event"
```
```json [JSON]
{
"summary": "{{title}} – {{featured | ternary('Featured', 'Regular')}} Event"
}
```
```js [JavaScript]
{
summary: "{{title}} – {{featured | ternary('Featured', 'Regular')}} Event",
}
```
:::
::: code-group
```yaml [YAML]
summary: "{{title}} {{published | ternary('', '(DRAFT)')}}"
```
```toml [TOML]
summary = "{{title}} {{published | ternary('', '(DRAFT)')}}"
```
```json [JSON]
{
"summary": "{{title}} {{published | ternary('', '(DRAFT)')}}"
}
```
```js [JavaScript]
{
summary: "{{title}} {{published | ternary('', '(DRAFT)')}}",
}
```
:::
### `date`
Format a date string, with an optional timezone.
```
{{value | date('')}}
```
```
{{value | date('', '')}}
```
The `format` argument specifies the desired date format using [Day.js formatting tokens](https://day.js.org/docs/en/display/format).
The optional `timezone` argument allows you to specify the time zone for formatting. It only supports `utc` for Coordinated Universal Time. If no timezone is provided, the local timezone will be used.
If an invalid date is provided, an empty string will be returned.
Configuration examples:
::: code-group
```yaml [YAML]
summary: "{{publish_date | date('YYYY-MM-DD')}}"
```
```toml [TOML]
summary = "{{publish_date | date('YYYY-MM-DD')}}"
```
```json [JSON]
{
"summary": "{{publish_date | date('YYYY-MM-DD')}}"
}
```
```js [JavaScript]
{
summary: "{{publish_date | date('YYYY-MM-DD')}}",
}
```
:::
::: code-group
```yaml [YAML]
summary: "{{publish_date | date('YYYY-MM-DD', 'utc')}}"
```
```toml [TOML]
summary = "{{publish_date | date('YYYY-MM-DD', 'utc')}}"
```
```json [JSON]
{
"summary": "{{publish_date | date('YYYY-MM-DD', 'utc')}}"
}
```
```js [JavaScript]
{
summary: "{{publish_date | date('YYYY-MM-DD', 'utc')}}",
}
```
:::
::: warning Breaking change from Netlify/Decap CMS
Sveltia CMS (and Decap CMS 3.1.1) has replaced the Moment.js library with Day.js for date formatting and parsing. Since [Day.js tokens](https://day.js.org/docs/en/display/format) are not 100% compatible with [Moment.js tokens](https://momentjs.com/docs/#/displaying/format/), this could be a breaking change in certain cases. Check your date/time format if you’re migrating from Netlify CMS or earlier versions of Decap CMS.
:::
## Examples
### Summary with Multiple Transformations
The below example demonstrates how to use multiple string transformations in the `summary` option of a collection:
::: code-group
```yaml [YAML]
collections:
- name: blog
label: Blog
summary: "{{title | upper}} — {{publish_date | date('YYYY-MM-DD')}} — {{body | truncate(20, '...')}}"
fields:
- name: title
label: Title
widget: string
- name: publish_date
label: Publish Date
widget: datetime
- name: body
label: Body
widget: richtext
```
```toml [TOML]
[[collections]]
name = "blog"
label = "Blog"
summary = "{{title | upper}} — {{publish_date | date('YYYY-MM-DD')}} — {{body | truncate(20, '...')}}"
[[collections.fields]]
name = "title"
label = "Title"
widget = "string"
[[collections.fields]]
name = "publish_date"
label = "Publish Date"
widget = "datetime"
[[collections.fields]]
name = "body"
label = "Body"
widget = "richtext"
```
```json [JSON]
{
"collections": [
{
"name": "blog",
"label": "Blog",
"summary": "{{title | upper}} — {{publish_date | date('YYYY-MM-DD')}} — {{body | truncate(20, '...')}}",
"fields": [
{
"name": "title",
"label": "Title",
"widget": "string"
},
{
"name": "publish_date",
"label": "Publish Date",
"widget": "datetime"
},
{
"name": "body",
"label": "Body",
"widget": "richtext"
}
]
}
]
}
```
```js [JavaScript]
{
collections: [
{
name: "blog",
label: "Blog",
summary: "{{title | upper}} — {{publish_date | date('YYYY-MM-DD')}} — {{body | truncate(20, '...')}}",
fields: [
{
name: "title",
label: "Title",
widget: "string",
},
{
name: "publish_date",
label: "Publish Date",
widget: "datetime",
},
{
name: "body",
label: "Body",
widget: "richtext",
},
],
},
],
}
```
:::
It will transform the `title` to uppercase, format the `publish_date` to `YYYY-MM-DD`, and truncate the `body` to 20 characters with an ellipsis. For example, if an entry has the following values:
::: code-group
```yaml [YAML]
title: My First Blog Post
publish_date: 2024-06-15T10:30:00Z
body: This is the content of my first blog post. It has a lot of interesting information
```
```toml [TOML]
title = "My First Blog Post"
publish_date = 2024-06-15T10:30:00Z
body = "This is the content of my first blog post. It has a lot of interesting information"
```
```json [JSON]
{
"title": "My First Blog Post",
"publish_date": "2024-06-15T10:30:00Z",
"body": "This is the content of my first blog post. It has a lot of interesting information"
}
```
:::
The resulting summary will be:
```
MY FIRST BLOG POST — 2024-06-15 — This is the content...
```
### Chaining Transformations
You can chain multiple transformations together by separating them with a pipe (`|`). For example, to convert a title to uppercase and then truncate it to 10 characters, you can use:
::: code-group
```yaml [YAML]
summary: "{{title | upper | truncate(10, '...')}}"
```
```toml [TOML]
summary = "{{title | upper | truncate(10, '...')}}"
```
```json [JSON]
{
"summary": "{{title | upper | truncate(10, '...')}}"
}
```
```js [JavaScript]
{
summary: "{{title | upper | truncate(10, '...')}}",
}
```
:::
---
---
url: /en/docs/successor-to-netlify-cms.md
description: >-
Learn how Sveltia CMS was born as a modern successor to Netlify CMS, and how
it differs from the official successor, Decap CMS.
---
# Successor to Netlify CMS
Sveltia CMS was built from scratch as a modern successor to Netlify CMS, which was abandoned in early 2022 (and later rebranded as Decap CMS). We have picked up where they left off and have already solved hundreds of issues reported in the predecessor’s repository, ranging from critical bugs to top feature requests.
Here’s our backstory and what makes Sveltia CMS different.
::: info Netlify vs. Netlify CMS
**Netlify CMS** was one of the open source projects maintained by **Netlify**, the popular web hosting and automation platform. While the CMS is no longer part of their portfolio, Netlify itself remains a thriving company offering various services for modern web development.
:::
::: info Independent Project
We are not affiliated with Netlify or any of its partners. Sveltia CMS is an independent, open source project created and maintained by [Kohei Yoshino](https://github.com/kyoshino). It’s platform-independent and not tied to any specific hosting provider.
:::
## TL;DR
* A complete, modern rewrite of [Netlify CMS](https://www.netlify.com/blog/2017/12/07/open-source-netlify-cms-hits-1.0-bringing-git-based-content-management-to-static-sites-everywhere/) (now [Decap CMS](https://github.com/decaporg/decap-cms))
* Actively maintained and outperforming stagnant Decap CMS in all aspects
* De facto successor to Netlify CMS that works better by design
* Drop-in replacement for common Netlify/Decap CMS use cases
* Solved 290+ [Netlify/Decap CMS issues](https://github.com/decaporg/decap-cms/issues) (625+ including duplicates)
* Significant UX/DX improvements with more enhancements planned
* Many sites already migrated from Netlify/Decap CMS to Sveltia CMS

## Motivation
Sveltia CMS was born in November 2022, when the progress of [Netlify CMS](https://www.netlify.com/blog/2017/12/07/open-source-netlify-cms-hits-1.0-bringing-git-based-content-management-to-static-sites-everywhere/) was stalled for more than six months. [@kyoshino](https://github.com/kyoshino)’s clients wanted to replace their Netlify CMS instances without much effort, mainly to get better internationalization (i18n) support.
To achieve radical improvements in UX, performance, i18n and other areas, it was ultimately decided to build an alternative from the ground up, while ensuring an easy migration path from the other. After proving the idea with a rapid [Svelte](https://svelte.dev/) prototype, development was accelerated to address their primary use cases. The new product has since been named Sveltia CMS and released as open source software to encourage wider adoption.
We loved the simple, unique setup of Netlify CMS that turned a Git repository into a database with a single-page app served from a CDN plus a plain YAML config file. In support of the [Jamstack](https://jamstack.org/) concept, we wanted to revive it, modernize it, and take it to the next level.
## Objectives
Our goal is not just to create another CMS that is compatible with Netlify CMS, but to succeed Netlify CMS as a whole by addressing its numerous issues reported to its repository over the years. Here’s how we’re doing:
### Ensuring High [Compatibility with Netlify/Decap CMS](/en/docs/migration/netlify-decap-cms#compatibility)
* Inherits the [CDN-served SPA architecture](/en/docs/architecture#how-sveltia-cms-works) of Netlify CMS, with various improvements
* Aims to work as a drop-in replacement for common use cases, as long as deprecated features are not in use
* The vast majority of existing configurations work out of the box
* Some missing features will be implemented before or shortly after GA
### Tackling as Many [Netlify/Decap CMS Issues](https://github.com/decaporg/decap-cms/issues) as Possible
* So far, **290+ issues, or 625+ if including duplicates, have been effectively solved** in Sveltia CMS (Yes, you read it right)
* Target:
* 300 issues, or 600 if including duplicates, by v1.0 — Almost there! 🚀
* 450 issues, or 950 if including duplicates, in the future 💪
* or every single issue that’s relevant, fixable, and worth dealing with 🔥
* Issues include everything:
* Outstanding issues from feature requests to bug reports
* [Issues closed as stale](https://github.com/decaporg/decap-cms/issues?q=is%3Aissue+%22Closing+as+stale%22) or without an optimal solution
* [Discussions](https://github.com/decaporg/decap-cms/discussions)
* Stalled [pull requests](https://github.com/decaporg/decap-cms/pulls)
* Many of the bugs, including the annoying crashes, have already been solved
* The remaining bugs are mostly related to [unimplemented features](/en/docs/migration/netlify-decap-cms#current-limitations)
* Many of their [top-voted features](https://github.com/decaporg/decap-cms/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) are [on our table](/en/docs/roadmap) or already implemented in Sveltia CMS
## What About Decap CMS?
Due to its unfortunate abandonment in early 2022, Netlify CMS spawned three successors. Sveltia CMS is one of them, along with Static CMS and Decap CMS. Here’s a brief comparison of the three projects:
* [Static CMS](https://github.com/StaticJsCMS/static-cms): a community fork
* Initial commit made in September 2022
* ❌ Discontinued in September 2024 after making meaningful improvements
* **Sveltia CMS**: not a fork but a **complete rewrite**
* Started in November 2022, first appeared on GitHub in March 2023
* Personal project of an experienced UX engineer
* ✅ Actively developed with [frequent releases](https://github.com/sveltia/sveltia-cms/releases) and [numerous improvements](#improvements-over-netlify-decap-cms)
* ✅ Relevant issues are being resolved regardless of their age or status
* ✅ Most of new bug reports are addressed promptly, usually within a day
* ✅ Pull requests are reviewed quickly, though we only accept trivial ones for now
* ✅ Provides comprehensive documentation, including [Netlify CMS migration guide](/en/docs/migration/netlify-decap-cms)
* ✅ The [Showcase](/en/showcase) page features various real-world sites using Sveltia CMS
* ✅ An [extensive roadmap](/en/docs/roadmap) is available to keep users informed
* ✅ No known unpatched security vulnerabilities, with dependencies kept up-to-date
* [Decap CMS](https://github.com/decaporg/decap-cms): a rebranded version
* [Announced in February 2023](https://www.netlify.com/blog/netlify-cms-to-become-decap-cms/) as an official continuation with a Netlify agency partner taking ownership
* Maintained by a company with three developers
* ⚠️ Seemingly random issues were [closed as stale](https://github.com/decaporg/decap-cms/issues?q=is%3Aissue+%22Closing+as+stale%22) following the takeover
* ⚠️ It took six months to ship the first release (v3.0) under the new name
* ⚠️ Mostly low activity with [only occasional releases](https://github.com/decaporg/decap-cms/releases) and a few minor improvements
* ⚠️ Bug reports continue to pile up, often without any response
* ⚠️ Pull requests, including those from maintainers, sit idle for months or years
* ⚠️ Keeps poor documentation without any migration guide or compatibility notes
* ⚠️ The [Examples](https://decapcms.org/docs/examples/) page only has demos, not real-world users
* ⚠️ No public roadmap is available, leaving users in the dark
* ❌ A moderate severity [XSS vulnerability](https://github.com/advisories/GHSA-xp8g-32qh-mv28), a [local server vulnerability](https://github.com/decaporg/decap-cms/issues/7692), high severity dependency vulnerabilities and fatal crashes remain unaddressed
While Decap CMS emerged as the official successor, it has largely stagnated and failed to revive the original Netlify CMS project as hoped. Unfortunately, it’s not even in maintenance mode, as critical bugs and security vulnerabilities have gone unaddressed for a long time. This leaves users with no choice but to switch to Sveltia CMS or other alternatives.
Created months before the announcement of Decap CMS, Sveltia CMS has firmly established itself as the de facto successor. We have a clear focus on delivering a superior experience for users seeking a modern alternative to Netlify CMS. Regardless of other projects in this field, we are committed to continuously enhancing the platform for the benefit of our users.
## True Successor, Better by Design
Among the three successors, Sveltia CMS is the only project that doesn’t inherit the complexity, technical debt, and numerous bugs of Netlify CMS.
* We rebuilt the app from scratch using a [modern framework](https://svelte.dev/)
* We don’t reuse any part of the predecessor’s codebase
* We incorporate [i18n support](/en/docs/i18n) into the core instead of adding it as an afterthought
* We closely monitor and analyze the predecessor’s issue tracker
* We rearchitect the entire user experience (UX) and developer experience (DX)
This “total reboot” has enabled us to implement [hundreds of improvements](#improvements-over-netlify-decap-cms) without getting stuck in a legacy system. Furthermore:
* We carry forward the original vision of Netlify CMS as a simple, Git-based content management solution for Jamstack sites
* We dedicate significant time and effort to modernizing the platform, including documentation and developer tools
* We continue to address [issues](https://github.com/decaporg/decap-cms/issues) reported in the predecessor’s repository
* We materialize the enhancements that Netlify CMS users have long desired
For that reason, Sveltia CMS is the **true successor** to Netlify CMS, not just a spiritual successor or mere alternative, albeit unofficial. Whether you’re migrating from the original Netlify CMS or another successor, Sveltia CMS offers the best overall experience.
## Adoption
[GitHub search](https://github.com/search?q=sveltia\&type=commits\&s=committer-date\&o=desc) shows that Netlify/Decap CMS users are migrating to Sveltia CMS every day to take advantage of its numerous improvements. Many sites have already made the switch. Visit our [Showcase](/en/showcase) page to see some of them!
## Improvements over Netlify/Decap CMS
Netlify/Decap CMS users will definitely be pleased and surprised by the numerous improvements we have made, from the small to the large. We make *everything* better.
::: info Note
This lengthy section compares Sveltia CMS with both Netlify CMS and Decap CMS. Some of the listed issues may have been resolved in the current version of Decap CMS.
:::
### Better UX
* Created and actively maintained by an [experienced UX engineer](https://github.com/kyoshino) who loves code, design, marketing, localization, documentation and everything in between. You can expect constant improvements to the user experience (UX) and developer experience (DX) across the platform.
* The maintainer tries to respond to bug reports as quickly as possible. While there are no guarantees, the typical turnaround time for a bug fix is less than 24 hours.
* Frequent releases deliver new features and enhancements to users more quickly. Meanwhile, Decap CMS’s release interval has been irregular and often long, sometimes exceeding two months, even between patch releases, which can be frustrating for both users and contributors.
* Many of our minor [releases](https://github.com/sveltia/sveltia-cms/releases) address one or more Netlify/Decap CMS issues, giving you even more reasons to switch from the legacy predecessor.
* Offers a modern, intuitive user interface that utilizes the full viewport,\[^178] inspired in part by the Netlify CMS v3 prototype.\[^1]\[^211]\[^212]\[^213]\[^214]
* Provides immersive dark mode.\[^2] The UI theme follows the user’s system preference by default and can be changed in the application settings.
* Users can easily manage content on-the-go with mobile and tablet support.\[^18]\[^215]
* For a smoother experience, we even go beyond responsive design with optimized navigation, floating action buttons, [view transitions](https://developer.chrome.com/docs/web-platform/view-transitions), larger buttons, and other tweaks. We’ll continue to fully optimize the app for small screens and touch devices.
* If you’re already signed in on your desktop, open the Account menu in the top right corner of the CMS, click Sign In with Mobile, and scan the QR code for passwordless sign-in. Your settings will be automatically copied.
* Made with [Svelte](https://svelte.dev/), not React, means we can spend more time on UX rather than tedious state management. It also allows us to avoid common fatal React application crashes.\[^113]\[^129] Best of all, Svelte offers great performance.
* Other crashes in Netlify/Decap CMS are also irrelevant to us, making Sveltia CMS much more stable.\[^112]\[^203]\[^204]\[^260] Netlify/Decap CMS continues to receive crash reports on a daily basis, with no effective solution in sight.
* We build [our own UI component library](https://github.com/sveltia/sveltia-ui), including custom dialogs, to ensure optimal usability without compromising accessibility.\[^277]\[^196]\[^205]\[^206]\[^207]\[^208]\[^209]\[^210]
* Users can personalize the application with various settings, including appearance and language. [Developer Mode](/en/docs/ui#developer-mode) can also be enabled, which enables certain features and displays the CMS version number.\[^270]
* Never miss out on the latest features and bug fixes by being notified when an update to the CMS is available.\[^31] Then update to the latest version with a single click.\[^66]
### Better Performance
* Built completely from scratch with [Svelte](https://svelte.dev/) instead of forking React-based Netlify/Decap CMS. The app starts fast and stays fast with [no virtual DOM overhead](https://svelte.dev/blog/virtual-dom-is-pure-overhead). Note that Svelte is a compiler and Sveltia CMS is [framework-agnostic](/en/docs/frameworks); it’s served as a vanilla JavaScript bundle.
* Small footprint: The bundle size is less than 500 KB when minified and [brotlied](https://en.wikipedia.org/wiki/Brotli), which is much lighter than Netlify CMS (1.5 MB), Decap CMS (1.5 MB) and Static CMS (2.6 MB).\[^57] This significant reduction in size is thanks to the combination of [Svelte 5](https://svelte.dev/blog/svelte-5-is-alive) and [Vite](https://vite.dev/). Sveltia CMS also dynamically loads certain dependencies only when needed, further reducing the initial load time.
* Uses the GraphQL API for GitHub and GitLab to quickly fetch content at once, so that entries and assets can be listed and searched instantly\[^32]\[^65] (the useless `search` configuration option is therefore ignored). It also avoids the slowness and potential API rate limit violations caused by hundreds of requests with Relation fields.\[^14]
* Saving entries and assets to GitHub is also much faster thanks to the [GraphQL mutation](https://github.blog/changelog/2021-09-13-a-simpler-api-for-authoring-commits/).
* The Gitea/Forgejo backend is also faster because it utilizes an efficient API method introduced in Gitea 1.24 and Forgejo 12.0.
* Our [local repository workflow](/en/docs/workflows/local) utilizes the modern [File System Access API](https://developer.chrome.com/docs/capabilities/web-apis/file-system-access) to read and write files natively through the web browser, rather than using a slow, ad hoc REST API through a proxy server.
* Sorting, filtering and grouping of entries is done instantly without reloading the entire content.
* Uses caching, lazy loading and infinite scrolling techniques. A list of repository files is stored locally for faster startup and bandwidth savings.
* Thumbnails of assets, including videos and PDF files, are generated and cached for faster rendering of the Asset Library and other parts of the CMS.\[^39]\[^38]
* No typing lag on input fields, especially within nested lists and objects.\[^77]
* The entry preview doesn’t use an `