Skip to content

Astro Integration Guide ​

This guide provides resources and information for integrating Sveltia CMS with Astro, a modern static site builder.

Starter Templates ​

Here are some starter templates built by the community using Astro:

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. Most of the listed sites include links to their source code, so you can explore how they implemented Sveltia CMS with Astro.

Setup ​

Adding the Admin Page ​

Astro serves static files from the public folder. Create public/admin/index.html and public/admin/config.yml as described in the Getting Started guide.

The Astro development server doesn’t serve index.html for a folder path in public, so open /admin/index.html instead of /admin/ during development. On the built site, most hosting services serve the admin page at /admin/ as usual.

If your site uses the <ClientRouter /> component for view transitions, add the data-astro-reload attribute to links to the admin page, so they are opened as regular pages instead of being handled by the router:

html
<a href="/admin/" data-astro-reload>Edit content</a>

Configuration ​

Astro manages Markdown content with content collections defined in src/content.config.ts. Each collection loads files with a loader like glob() and validates their front matter with a Zod schema:

ts
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: ({ image }) =>
    z.object({
      title: z.string(),
      description: z.string().optional(),
      pubDate: z.coerce.date(),
      heroImage: image().optional(),
      tags: z.array(z.string()).optional(),
    }),
});

export const collections = { blog };

The matching Sveltia CMS entry collection uses the base folder of the loader. The following example stores each post in a folder of its own as index.md, with entry-relative media folders, so the hero image is saved next to the post and can be optimized by Astro with the image() schema helper:

yaml
media_folder: public/images
public_folder: /images
output:
  omit_empty_optional_fields: true
collections:
  - name: blog
    label: Blog
    folder: src/content/blog
    path: '{{slug}}/index'
    media_folder: ''
    public_folder: ''
    create: true
    fields:
      - { name: title, label: Title }
      - { name: description, label: Description, required: false }
      - { name: pubDate, label: Publish Date, widget: datetime }
      - { name: heroImage, label: Hero Image, widget: image, required: false }
      - { name: tags, label: Tags, widget: list, required: false }
      - { name: body, label: Body, widget: markdown }

If you store posts as single files instead, remove the path, media_folder and public_folder options from the collection. Images are then saved in the public/images folder defined at the top level, which Astro serves as is without optimization, so use z.string() instead of image() in the schema.

Omitting Empty Optional Fields ​

Sveltia CMS saves an empty value, such as an empty string, an empty array or null, for an optional field that is left empty. Zod’s .optional() only accepts a missing property, so these values can cause a build error like “data does not match collection schema”. Set the omit_empty_optional_fields output option to true, as in the example above, to leave empty optional fields out of the front matter.

Support for Astro ​

We have implemented specific features to enhance the integration of Sveltia CMS with Astro: