Skip to content

Migrating from Pages CMS ​

Pages CMS is a Git-based CMS for static sites that was inspired by Netlify CMS. Its content model is close to that of Sveltia CMS: content is stored as Markdown, YAML, JSON or TOML files in a Git repository, and the content structure is defined in a configuration file. This makes the migration relatively straightforward, as existing content files can be kept as they are in most cases.

This guide explains the differences between the two platforms and how to convert a Pages CMS configuration file to the Sveltia CMS format. It’s based on Pages CMS 2.x.

Stable Version Not Yet Available

Sveltia CMS is still in beta. Although it’s already being used in production by many users, there might still be breaking changes before the stable 1.0 release. We recommend keeping an eye on the release information for any updates.

Examples ​

See the following examples of sites that have been migrated from Pages CMS to see how other users have successfully transitioned to Sveltia CMS.

Key Differences ​

Architecture ​

Pages CMS is a Next.js application. Users sign in to the hosted app at app.pagescms.org or to a self-hosted instance, which accesses repositories through a GitHub App and keeps a cache in a PostgreSQL database.

Sveltia CMS is a single-page application that runs entirely in the browser and talks to the Git hosting service directly. It’s installed on the site itself, typically at /admin/, and there is no hosted app, database or GitHub App to set up. See Architecture for details.

As a result, the migration involves the following changes:

  • Installation: Instead of installing a GitHub App, add an admin/index.html file to the site. See the Start Guide.
  • Configuration: The configuration file is admin/config.yml instead of .pages.yml at the root of the repository. The structure is different, so it must be converted. See below.
  • Authentication: Users sign in with their GitHub account using an OAuth client or a personal access token. See GitHub Backend for the available methods.
  • Git hosting services: Pages CMS supports GitHub only, while Sveltia CMS also supports GitLab, Gitea and Forgejo. This gives you the option to move the repository to another service later.

Collaborators ​

Pages CMS allows repository owners to invite collaborators by email, so people without a GitHub account can edit content through the GitHub App. Sveltia CMS doesn’t have an equivalent feature yet, as it requires each user to have an account with write access to the repository on the Git hosting service. Invite your collaborators to the repository on GitHub instead. See Invite Team Members.

We plan to support user management and roles with Sveltia CMS Additions, our free server-side component.

Features Not Available in Sveltia CMS ​

The following Pages CMS features have no direct equivalent in Sveltia CMS:

  • Branch switching: Pages CMS reads .pages.yml from the branch being edited and lets users switch branches. Sveltia CMS works on the single branch specified with the branch backend option. The Editorial Workflow can be used to review changes in separate branches before publishing them.
  • Actions: Pages CMS can run GitHub Actions workflows from buttons with custom inputs. When automatic deployments are disabled, Sveltia CMS can trigger a deployment with the Publish Changes button, which sends a repository_dispatch event to GitHub Actions or calls a webhook, but it doesn’t support other workflows or inputs.
  • Commit identity: Pages CMS can commit as the GitHub App. Sveltia CMS always commits as the signed-in user.
  • Raw and datagrid editors: Pages CMS provides a plain text editor for files without fields and a spreadsheet-like editor for CSV files. Sveltia CMS can manage such files with the raw format and a single Code or Text field named body, but there is no table editor.
  • Rich text in HTML: The format: html option of the rich-text field type is not supported yet, as the RichText field type currently outputs Markdown only. HTML output is planned for a future release.
  • Random filenames: The rename: random media option is not supported. The rename: safe option is supported as slugify_filename.
  • Nested sidebar groups: The group content type is not supported. Use dividers to separate the collection list, or put related files into one file collection.

On the other hand, Sveltia CMS offers many features that are not available in Pages CMS, including internationalization, Editorial Workflow, entry previews, external media storage, image optimization, local development without a server and GitLab, Gitea and Forgejo support. See Features for the full list.

Converting the Configuration ​

Pages CMS and Sveltia CMS use different option names, but most of the concepts map directly to each other. This section lists the equivalents. You can also ask your AI assistant to convert the .pages.yml file using this guide; see Working with AI for tools that help it write a valid configuration.

Example ​

Here is a typical Pages CMS configuration:

yaml
media:
  input: public/images
  output: /images
content:
  - name: posts
    label: Posts
    type: collection
    path: content/posts
    filename: '{year}-{month}-{day}-{primary}.md'
    view:
      fields: [title, date]
      sort: [date, title]
      default: { sort: date, order: desc }
    fields:
      - { name: title, label: Title, type: string, required: true }
      - { name: date, label: Date, type: date }
      - { name: draft, label: Draft, type: boolean }
      - { name: tags, label: Tags, type: string, list: true }
      - { name: cover, label: Cover Image, type: image }
      - { name: body, label: Body, type: rich-text }
  - name: settings
    label: Site Settings
    type: file
    path: data/settings.json
    fields:
      - { name: title, label: Site Title, type: string }
      - { name: description, label: Description, type: text }

And here is the equivalent Sveltia CMS configuration:

yaml
backend:
  name: github
  repo: owner/repo
  branch: main
media_folder: /public/images
public_folder: /images
collections:
  - name: posts
    label: Posts
    folder: /content/posts
    slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
    summary: '{{title}} ({{date}})'
    sortable_fields:
      fields: [date, title]
      default: { field: date, direction: descending }
    fields:
      - { name: title, label: Title }
      - { name: date, label: Date, widget: datetime, type: date, default: '{{now}}', required: false }
      - { name: draft, label: Draft, widget: boolean, required: false }
      - { name: tags, label: Tags, widget: list, required: false }
      - { name: cover, label: Cover Image, widget: image, required: false }
      - { name: body, label: Body, widget: richtext, required: false }
  - name: settings
    label: Site Settings
    files:
      - name: settings
        label: Site Settings
        file: /data/settings.json
        fields:
          - { name: title, label: Site Title, required: false }
          - { name: description, label: Description, widget: text, required: false }

Note the following differences:

  • The backend option is required to tell Sveltia CMS where the content is stored.
  • Paths can start with a slash, and entry filenames are defined with the slug option without the extension.
  • The type field option becomes widget, which defaults to string.
  • Fields are required by default in Sveltia CMS, while they are optional in Pages CMS. Add required: false to fields that were not required: true.

Media ​

The media option is converted as follows. See Internal Media Storage for details.

Pages CMSSveltia CMS
media: media (string)media_folder: /media and public_folder: /media
inputmedia_folder
outputpublic_folder
extensions, categoriesThe accept option of each File/Image field, e.g. accept: .pdf,.docx or image/*
rename: true or rename: safeslugify_filename: true under media_libraries.default.config
rename: randomNot supported
Multiple named media sourcesAsset collections
media and path options of a fieldField-level media_folder and public_folder

Content Types ​

A Pages CMS collection is an entry collection, and a file is a file in a file collection or a singleton. Singletons are a good fit if your Pages CMS configuration has many standalone files.

Pages CMSSveltia CMS
name, labelname, label
path (collection)folder
path (file)file in a files item
filenameslug for the template, plus extension
filename.field: falseslug.editable: false
filename.field: createslug.editable: [create]
filename.field: trueslug.editable: true (default)
formatformat. The values yaml-frontmatter, json-frontmatter, toml-frontmatter, yaml, json, toml and raw are the same. Use raw for code and datagrid
delimitersfrontmatter_delimiter
subfolders: trueNested collections or the path option
list: true on a fileA top-level List field, or a single-file collection for a JSON file
excludeNo filename-based option. Use filter to filter entries by field value
operations.create, operations.deletecreate, delete
operations.renameslug.editable
groupNot supported. See above

Filename Templates ​

The filename template tags are converted to slug template tags as follows:

Pages CMSSveltia CMS
{primary}, {slug}{{slug}}, which uses the title field or the field specified with identifier_field. Pages CMS falls back to the first field if there is no title field, so set identifier_field in that case
{year}, {month}, {day}, {hour}, {minute}, {second}{{year}}, {{month}}, {{day}}, {{hour}}, {{minute}}, {{second}}
{fields.name}, {name}{{fields.name}}, {{name}}

The file extension is not part of the template. For example, filename: '{year}-{month}-{day}-{primary}.md' becomes slug: '{{year}}-{{month}}-{{day}}-{{slug}}', and the md extension is used by default.

Pages CMS uses the default filename {year}-{month}-{day}-{primary}.md when the option is omitted, while Sveltia CMS uses {{slug}}. Set the slug option explicitly to keep the same naming convention for new entries.

The date and time tags use the local time in Pages CMS and UTC in Sveltia CMS by default. Set the timezone global slug option to local to use the local time.

Pages CMS converts field values in filenames to lowercase ASCII letters, numbers and hyphens, while Sveltia CMS keeps Unicode characters by default. Set the encoding global slug option to ascii and clean_accents to true for similar filenames.

Views ​

The view options are converted as follows. See Entry Listings and Entry Views for details.

Pages CMSSveltia CMS
view.primaryidentifier_field
view.fieldssummary template, e.g. '{{title}} ({{date}})'
view.sortsortable_fields
view.default.sort, view.default.ordersortable_fields.default.field, sortable_fields.default.direction (ascending or descending)
view.searchNot needed, as all entries are searchable
view.layout: tree, view.nodeNested collections

Fields ​

The common field options are converted as follows. See Fields for details.

Pages CMSSveltia CMS
name, labelname, label
typewidget
descriptionhint
required (default: false)required (default: true)
readonlyreadonly
pattern: '^[a-z]+$'pattern: ['^[a-z]+$', 'Error message']. See pattern
pattern: { regex, message }pattern: [regex, message]
hidden: truewidget: hidden with a default value. See Hidden
defaultdefault
list: trueA List field. See below
componentYAML anchors and aliases. See below

The field types are converted as follows:

Pages CMSSveltia CMS
stringstring (default)
texttext
rich-textrichtext (Markdown only)
codecode
numbernumber
booleanboolean
datedatetime
selectselect
referencerelation
imageimage
filefile
objectobject
blocklist or object with the types option
uuiduuid
Custom field typesCustom field types

The minlength, maxlength, min, max and step options work the same way where supported. Other type-specific options are described below.

Code ​

The Code field in Sveltia CMS saves an object containing the code and the language by default. To save the code as a plain string, as in Pages CMS, set output_code_only: true. The options.format option becomes default_language.

yaml
- name: snippet
  label: Snippet
  widget: code
  default_language: javascript
  output_code_only: true
  allow_language_selection: false

Number ​

The Number field in Sveltia CMS accepts integers only by default. Set value_type: float to allow decimal numbers.

Date ​

The date field type is converted to the DateTime field type:

  • Without options.time, add type: date. The value is saved as YYYY-MM-DD, as in Pages CMS.
  • With options.time: true, the value is saved in ISO 8601 format with seconds, such as 2025-08-15T14:30:00. Pages CMS saves 2025-08-15T14:30 by default. Use the format option if your site needs the same format.
  • The options.format option becomes format, but Pages CMS uses date-fns tokens while Sveltia CMS uses Day.js tokens. For example, yyyy-MM-dd'T'HH:mm becomes YYYY-MM-DD[T]HH:mm.
  • Pages CMS sets new entries to the current date by default. Add default: '{{now}}' to do the same.
yaml
- name: date
  label: Date
  widget: datetime
  type: date
  default: '{{now}}'

Select ​

The options.values option becomes options. Objects with name and label properties become objects with value and label properties. The multiple, min and max options work the same way. The placeholder option is not supported.

yaml
- name: status
  label: Status
  widget: select
  options:
    - { label: Draft, value: draft }
    - { label: Published, value: published }

Reference ​

The reference field type is converted to the Relation field type:

Pages CMSSveltia CMS
options.collectioncollection
options.valuevalue_field
options.labeldisplay_fields
options.searchsearch_fields, as an array
options.multiple, options.min, options.maxmultiple, min, max

The template tags are similar, but they are enclosed in double curly braces, e.g. {{fields.name}}.

Pages CMS saves the full path of the referenced file by default, such as content/authors/jane.md, while Sveltia CMS saves the entry slug, such as jane. If your existing content contains file paths, set value_field to a field that has the same value, or update the content to use slugs. The {primary} tag becomes the name of the identifier field, e.g. {{title}}.

Image and File ​

The options.multiple option becomes multiple, and options.multiple.max becomes max. The options.extensions and options.categories options become the accept option, e.g. accept: .pdf or accept: image/*. The options.media and options.path options become field-level media_folder and public_folder.

UUID ​

The options.editable: true option becomes readonly: false, as the UUID field is read-only by default.

Lists ​

A field with list: true becomes a List field. For a list of strings, use a List field without subfields. For a list of other types, specify the subfield with the field option. The list.min and list.max options become min and max.

yaml
# Pages CMS
- { name: tags, label: Tags, type: string, list: true }
- { name: photos, label: Photos, type: image, list: { max: 10 } }
- name: links
  label: Links
  type: object
  list: { collapsible: { collapsed: true, summary: '{fields.title}' } }
  fields:
    - { name: title, label: Title, type: string }
    - { name: url, label: URL, type: string }

# Sveltia CMS
- { name: tags, label: Tags, widget: list }
- name: photos
  label: Photos
  widget: list
  max: 10
  field: { name: photo, label: Photo, widget: image }
- name: links
  label: Links
  widget: list
  collapsed: true
  summary: '{{title}}'
  fields:
    - { name: title, label: Title }
    - { name: url, label: URL }

Note that a List field with the fields option, like links above, is an object list. See the List field documentation for the data output of each configuration.

Blocks ​

A block field with list: true becomes a List field with the types option. Each block becomes a type. The property that stores the block name is _block by default in Pages CMS and type in Sveltia CMS, so set the typeKey option to _block, or to the blockKey value if you have customized it.

yaml
# Pages CMS
- name: sections
  label: Sections
  type: block
  list: true
  blocks:
    - name: hero
      label: Hero
      fields:
        - { name: heading, type: string }
    - name: text
      label: Text
      fields:
        - { name: body, type: rich-text }

# Sveltia CMS
- name: sections
  label: Sections
  widget: list
  typeKey: _block
  types:
    - name: hero
      label: Hero
      fields:
        - { name: heading, label: Heading }
    - name: text
      label: Text
      fields:
        - { name: body, label: Body, widget: richtext }

A block field without list: true becomes an Object field with the types option.

Components ​

Sveltia CMS doesn’t have a components option, but YAML anchors and aliases can be used to reuse field definitions. Define a field with an anchor (&name) once, then reference it with an alias (*name). The merge key (<<) allows you to override some options, like the component option in Pages CMS.

yaml
collections:
  - name: pages
    label: Pages
    folder: /content/pages
    fields:
      - &seo
        name: seo
        label: SEO
        widget: object
        fields:
          - { name: title, label: Title }
          - { name: description, label: Description, widget: text }
  - name: posts
    label: Posts
    folder: /content/posts
    fields:
      - <<: *seo
        label: Meta

Settings ​

Pages CMSSveltia CMS
settings.content.mergeNot needed. Sveltia CMS always keeps the properties that are not defined in the configuration, placing them after the configured fields.
settings.commit.templatescommit_messages backend option. The template tags are different, e.g. {{collection}}, {{slug}} and {{path}}
settings.commit.identityNot supported. Commits are always made by the signed-in user
settings.hideNot needed, as there is no settings page

Migration Steps ​

  1. Review the key differences above to make sure there are no blockers.
  2. Follow the Start Guide to add Sveltia CMS to your site, including the admin/index.html file and the backend configuration.
  3. Convert .pages.yml to admin/config.yml using the mapping above. Enable JSON schema validation in your code editor to catch mistakes early.
  4. Test the configuration with the local development workflow. Open some existing entries and save them to make sure the data output is unchanged, as there might be differences in formatting. Use the output options to adjust the output if needed.
  5. Deploy the site and set up authentication for production.
  6. Invite your Pages CMS collaborators to the GitHub repository, and share the admin URL with them.
  7. Once everything works as expected, delete .pages.yml from the repository and uninstall the Pages CMS GitHub App from your account or organization.

If you encounter any issues during the migration, feel free to ask in our Discussions or report a bug.