How to use PagesCMS with Astro

Learn how to integrate PagesCMS with Astro for a complete headless CMS solution. Step-by-step guide with code examples.

Published on August 26, 2025 by Michael Andreuzza

Introduction

Managing content for Astro websites can be a pain—editing files, dealing with frontmatter, and keeping everything working together. PagesCMS gives you a nice solution: a Git-based visual editor that works well with GitHub and plays nicely with Astro static sites.

In this Astro CMS tutorial, you’ll learn how to set up PagesCMS with Astro using the .pages.yml file and connect it with Astro’s content collections. The result? A simple content workflow with type checking, IDE help, and an easy commit-to-build process for your Astro blog or website.

Set up PagesCMS for Astro projects via .pages.yml

PagesCMS requires a .pages.yml file in your project repository. If it’s missing, PagesCMS prompts you to add one in its UI.

The file supports three main sections:

  • media: Defines where uploads like images and videos live
  • content: Configures your content types (collections, files, etc.)
  • components: Optionally defines reusable field sets

Example .pages.yml

media:
  - name: media
    label: Media
    input: src/media
    output: /media
    path: /media

content:
  - name: blog
    label: Blog Posts
    type: collection
    path: src/content/blog
    fields:
      - name: title
        label: Title
        type: string
      - name: pubDate
        label: Publication Date
        type: date
      - name: description
        label: Description
        type: string
      - name: tags
        label: Tags
        type: list
        items:
          type: string
      - name: body
        label: Body
        type: rich-text

Important things to know:

  • The media section makes sure your rich-text editor can handle media uploads properly
  • The content section creates a collection that goes to src/content/blog
  • Field types should match what you’ll set up in your Astro content setup

Set up Astro content collections

Astro content collections give you type checking, editor help, and better ways to work with your content in Astro websites.

Create Astro content schema (src/content/config.ts)

import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    pubDate: z.date(),
    description: z.string().optional(),
    tags: z.array(z.string()).optional(),
  }),
});

export const collections = { blog };

This Astro schema will check your frontmatter and give you IDE help when working with your Astro blog posts.

Directory structure alignment

Make sure both systems write and read from the same places:

project-root/
├── .pages.yml
├── astro.config.mjs
└── src/
    ├── content/
    │   ├── config.ts
    │   └── blog/
    │       ├── my-first-post.md
    │       └── another-post.md
    ├── media/
    │   └── (uploaded images)
    └── pages/
        ├── blog/
        │   ├── index.astro
        │   └── [slug].astro
        └── index.astro

When you edit content via PagesCMS, it commits new or updated .md files (with YAML frontmatter and content) into src/content/blog.

Create Astro blog pages

List all blog posts with Astro (src/pages/blog/index.astro)

---
import { getCollection } from 'astro:content';

const posts = await getCollection('blog');
const sortedPosts = posts.sort((a, b) => 
  new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime()
);
---

<html>
  <head>
    <title>Blog</title>
  </head>
  <body>
    <h1>Blog Posts</h1>
    <ul>
      {sortedPosts.map(post => (
        <li>
          <a href={`/blog/${post.slug}`}>
            <h2>{post.data.title}</h2>
            {post.data.description && <p>{post.data.description}</p>}
            <time>{post.data.pubDate.toLocaleDateString()}</time>
          </a>
        </li>
      ))}
    </ul>
  </body>
</html>

Individual Astro blog post page (src/pages/blog/[slug].astro)

---
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map(post => ({
    params: { slug: post.slug },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await post.render();
---

<html>
  <head>
    <title>{post.data.title}</title>
    <meta name="description" content={post.data.description} />
  </head>
  <body>
    <article>
      <h1>{post.data.title}</h1>
      <time>{post.data.pubDate.toLocaleDateString()}</time>
      
      {post.data.tags && (
        <div>
          {post.data.tags.map(tag => (
            <span class="tag">{tag}</span>
          ))}
        </div>
      )}
      <Content />
    </article>
  </body>
</html>

Astro PagesCMS simple workflow

Here’s how the Astro CMS workflow works:

  1. Create content: Use PagesCMS to create and edit Astro blog posts
  2. Git magic: PagesCMS saves changes to your Astro project GitHub repo
  3. Type checking: Astro content collections make sure content follows your rules
  4. Build and deploy: Your build process (GitHub Actions, Netlify, etc.) builds your Astro site and puts it live
  5. Live site: New content shows up on your Astro website

Conclusion

By putting PagesCMS and Astro together, you get:

  • Easy editing: Rich-text editor with media support for Astro content
  • Git workflow: Version control for all your Astro blog content changes
  • Auto publishing: Easy publish process for Astro websites

This Astro CMS setup works great for Astro blogs, documentation sites, or any Astro project where you want an free, OpenSource and easy CMS with the speed of a static site.

You get the best of both worlds: content creators get a friendly interface, while developers keep full control over the Astro codebase and how it gets published.

/Michael Andreuzza

Did you like this post? Please share it with your friends!