Skip to content

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 and File Collection 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.

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 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. 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.
  • 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 used in Sveltia CMS, the following Markdown conventions are applied to the output of the rich text editor:

  • 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 <br> tags). In your framework, you may need to enable the appropriate option 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:

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
[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
{
  "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
{
  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:

yaml
- name: images
  label: Images
  widget: list
  fields:
    - { name: image, label: Image, widget: image }
toml
[[fields]]
name = "images"
label = "Images"
widget = "list"
[[fields.fields]]
name = "image"
label = "Image"
widget = "image"
json
{
  "fields": [
    {
      "name": "images",
      "label": "Images",
      "widget": "list",
      "fields": [
        { "name": "image", "label": "Image", "widget": "image" }
      ]
    }
  ]
}
js
{
  fields: [
    {
      name: "images",
      label: "Images",
      widget: "list",
      fields: [
        { name: "image", label: "Image", widget: "image" },
      ],
    },
  ],
}

will produce the output:

yaml
images:
  - image: https://example.com/image1.jpg
  - image: https://example.com/image2.jpg
toml
[[images]]
image = "https://example.com/image1.jpg"
[[images]]
image = "https://example.com/image2.jpg"
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.

yaml
- name: images
  label: Images
  widget: list
  field: { name: image, label: Image, widget: image }
toml
[[fields]]
name = "images"
label = "Images"
widget = "list"
[fields.field]
name = "image"
label = "Image"
widget = "image"
json
{
  "fields": [
    {
      "name": "images",
      "label": "Images",
      "widget": "list",
      "field": { "name": "image", "label": "Image", "widget": "image" }
    }
  ]
}
js
{
  fields: [
    {
      name: "images",
      label: "Images",
      widget: "list",
      field: { name: "image", label: "Image", widget: "image" },
    },
  ],
}

The output will be:

yaml
images:
  - https://example.com/image1.jpg
  - https://example.com/image2.jpg
toml
images = ["https://example.com/image1.jpg", "https://example.com/image2.jpg"]
json
{
  "images": ["https://example.com/image1.jpg", "https://example.com/image2.jpg"]
}

root Option

When the root option is set to true, the List field is saved as a top-level list without a field name:

yaml
- name: John Doe
  id: 12345
- name: Jane Smith
  id: 67890
json
[
  { "name": "John Doe", "id": 12345 },
  { "name": "Jane Smith", "id": 67890 }
]

instead of

yaml
members:
  - name: John Doe
    id: 12345
  - name: Jane Smith
    id: 67890
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.

Released under the MIT License.