Using Pages CMS for Static Site Content Management

Friends, I’ve been on the hunt for a decent content management system for static sites for… well, about as long as we’ve all been calling them “static sites,” honestly.

I know, I know: there are a ton of content management system options available, and while I’ve tested several, none have really been the one, y’know? Weird pricing models, difficult customization, some even end up becoming a whole ‘nother thing to manage.

Also, I really enjoy building with site generators such as Astro or Eleventy, but pitching Markdown as the means of managing content is less-than-ideal for many “non-techie” folks.

A few expectations for content management systems might include:

  • Easy to use: The most important feature, why you might opt to use a content management system in the first place.
  • Minimal Requirements: Look, I’m just trying to update some HTML, I don’t want to think too much about database tables.
  • Collaboration: CMS tools work best when multiple contributors work together, contributors who probably don’t know Markdown or what GitHub is.
  • Customizable: No website is the same, so we’ll need to be able to make custom fields for different types of content.

Not a terribly long list of demands, I’d say; fairly reasonable, even. That’s why I was happy to discover Pages CMS.

According to its own home page, Pages CMS is the “The No-Hassle CMS for Static Site Generators,” and I’ll to attest to that. Pages CMS has largely been developed by a single developer, Ronan Berder, but is open source, and accepting pull requests over on GitHub.

Taking a lot of the “good parts” found in other CMS tools, and a single configuration file, Pages CMS combines things into a sleek user interface.

Pages CMS includes lots of options for customization, you can upload media, make editable files, and create entire collections of content. Also, content can have all sorts of different fields, check the docs for the full list of supported types, as well as completely custom fields.

There isn’t really a “back end” to worry about, as content is stored as flat files inside your git repository. Pages CMS provides folks the ability to manage the content within the repo, without needing to actually know how to use Git, and I think that’s neat.

User Authentication works two ways: contributors can log in using GitHub accounts, or contributors can be invited by email, where they’ll receive a password-less, “magic-link,” login URL. This is nice, as GitHub accounts are less common outside of the dev world, shocking, I know.

Oh, and Pages CMS has a very cheap barrier for entry, as it’s free to use.

Pages CMS and Astro content collections

I’ve created a repository on GitHub with Astro and Pages CMS using Astro’s default blog starter, and made it available publicly, so feel free to clone and follow along.

I’ve been a fan of Astro for a while, and Pages CMS works well alongside Astro’s content collection feature. Content collections make globs of data easily available throughout Astro, so you can hydrate content inside Astro pages. These globs of data can be from different sources, such as third-party APIs, but commonly as directories of Markdown files. Guess what Pages CMS is really good at? Managing directories of Markdown files!

Content collections are set up by a collections configuration file. Check out the src/content.config.ts file in the project, here we are defining a content collection named blog:

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

const blog = defineCollection({
// Load Markdown in the `src/content/blog/` directory.
loader: glob({ base: './src/content/blog', pattern: '**/*.md' }),
  // Type-check frontmatter using a schema
  schema: z.object({
    title: z.string(),
   description: z.string(),
    // Transform string to Date object
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
    heroImage: z.string().optional(),
  }),
});

export const collections = { blog };

The blog content collection checks the /src/content/blog directory for files matching the **/*.md file type, the Markdown file format. The schema property is optional, however, Astro provides helpful type-checking functionality with Zod, ensuring data saved by Pages CMS works as expected in your Astro site.

Pages CMS Configuration

Alright, now that Astro knows where to look for blog content, let’s take a look at the Pages CMS configuration file, .pages.config.yml:

content:
  - name: blog
    label: Blog
    path: src/content/blog
    filename: '{year}-{month}-{day}-{fields.title}.md'
    type: collection
    view:
      fields: [heroImage, title, pubDate]
    fields:
      - name: title
        label: Title
        type: string

      - name: description
        label: Description
        type: text

      - name: pubDate
        label: Publication Date
        type: date
        options:
          format: MM/dd/yyyy

      - name: updatedDate
        label: Last Updated Date
        type: date
        options:
          format: MM/dd/yyyy

      - name: heroImage
        label: Hero Image
        type: image

      - name: body
        label: Body
        type: rich-text

  - name: site-settings
    label: Site Settings
    path: src/config/site.json
    type: file
    fields:
      - name: title
        label: Website title
        type: string

      - name: description
        label: Website description
        type: string
        description: Will be used for any page with no description.

      - name: url
        label: Website URL
        type: string
        pattern: ^(https?://)?(www.)?[a-zA-Z0-9.-]+.[a-zA-Z]{2,}(/[^s]*)?$

      - name: cover
        label: Preview image
        type: image
        description: Image used in the social preview on social networks (e.g. Facebook, Twitter...)

media:
  input: public/media
  output: /media

There is a lot going on in there, but inside the content section, let’s zoom in on the blog object.

- name: blog
  label: Blog
  path: src/content/blog
  filename: '{year}-{month}-{day}-{fields.title}.md'
  type: collection
  view:
    fields: [heroImage, title, pubDate]
  fields:
    - name: title
      label: Title
      type: string

    - name: description
      label: Description
      type: text

    - name: pubDate
      label: Publication Date
      type: date
      options:
        format: MM/dd/yyyy

    - name: updatedDate
      label: Last Updated Date
      type: date
      options:
        format: MM/dd/yyyy

    - name: heroImage
      label: Hero Image
      type: image

    - name: body
      label: Body
      type: rich-text

We can point Pages CMS to the directory we want to save Markdown files using the path property, matching it up to the /src/content/blog/ location Astro looks for content.

path: src/content/blog

For the filename we can provide a pattern template to use when Pages CMS saves the file to the content collection directory. In this case, it’s using the file date’s year, month, and day, as well as the blog item’s title, by using fields.title to reference the title field. The filename can be customized in many different ways, to fit your scenario.

filename: '{year}-{month}-{day}-{fields.title}.md'

The type property tells Pages CMS that this is a collection of files, rather than a single editable file (we’ll get to that in a moment).

type: collection

In our Astro content collection configuration, we define our blog collection with the expectation that the files will contain a few bits of meta data such as: title, description, pubDate, and a few more properties.

We can mirror those requirements in our Pages CMS blog collection as fields. Each field can be customized for the type of data you’re looking to collect. Here, I’ve matched these fields up with the default Markdown frontmatter found in the Astro blog starter.

fields:
  - name: title
    label: Title
    type: string

  - name: description
    label: Description
    type: text

  - name: pubDate
    label: Publication Date
    type: date
    options:
      format: MM/dd/yyyy

  - name: updatedDate
    label: Last Updated Date
    type: date
    options:
      format: MM/dd/yyyy

  - name: heroImage
    label: Hero Image
    type: image

  - name: body
    label: Body
    type: rich-text

Now, every time we create a new blog item in Pages CMS, we’ll be able to fill out each of these fields, matching the expected schema for Astro.

Aside from collections of content, Pages CMS also lets you manage editable files, which is useful for a variety of things: site wide variables, feature flags, or even editable navigations.

Take a look at the site-settings object, here we are setting the type as file, and the path includes the filename site.json.

- name: site-settings
  label: Site Settings
  path: src/config/site.json
  type: file
  fields:
    - name: title
      label: Website title
      type: string

    - name: description
      label: Website description
      type: string
      description: Will be used for any page with no description.

    - name: url
      label: Website URL
      type: string
      pattern: ^(https?://)?(www.)?[a-zA-Z0-9.-]+.[a-zA-Z]{2,}(/[^s]*)?$

    - name: cover
      label: Preview image
      type: image
      description: Image used in the social preview on social networks (e.g. Facebook, Twitter...)

The fields I’ve included are common site-wide settings, such as the site’s title, description, url, and cover image.

Speaking of images, we can tell Pages CMS where to store media such as images and video.

media:
  input: public/media
  output: /media

The input property explains where to store the files, in the /public/media directory within our project.

The output property is a helpful little feature that conveniently replaces the file path, specifically for tools that might require specific configuration. For example, Astro uses Vite under the hood, and Vite already knows about the public directory and complains if it’s included within file paths. Instead, we can set the output property so Pages CMS will only point image path locations starting at the inner /media directory instead.

To see what I mean, check out the test post in the src/content/blog/ folder:

---
title: 'Test Post'
description: 'Here is a sample of some basic Markdown syntax that can be used when writing Markdown content in Astro.'
pubDate: 05/03/2025
heroImage: '/media/blog-placeholder-1.jpg'
---

The heroImage now property properly points to /media/... instead of /public/media/....

As far as configurations are concerned, Pages CMS can be as simple or as complex as necessary. You can add as many collections or editable files as needed, as well as customize the fields for each type of content. This gives you a lot of flexibility to create sites!

Connecting to Pages CMS

Now that we have our Astro site set up, and a .pages.config.yml file, we can connect our site to the Pages CMS online app. As the developer who controls the repository, browse to https://app.pagescms.org/ and sign in using your GitHub account.

You should be presented with some questions about permissions, you may need to choose between giving access to all repositories or specific ones. Personally, I chose to only give access to a single repository, which in this case is my astro-pages-cms-template repo.

After providing access to the repo, head on back to the Pages CMS application, where you’ll see your project listed under the “Open a Project” headline.

Clicking the open link will take you into the website’s dashboard, where we’ll be able to make updates to our site.

Creating content

Taking a look at our site’s dashboard, we’ll see a navigation on the left side, with some familiar things.

  • Blog is the collection we set up inside the .pages.config.yml file, this will be where we we can add new entries to the blog.
  • Site Settings is the editable file we are using to make changes to site-wide variables.
  • Media is where our images and other content will live.
  • Settings is a spot where we’ll be able to edit our .pages.config.yml file directly.
  • Collaborators allows us to invite other folks to contribute content to the site.

We can create a new blog post by clicking the Add Entry button in the top right

Here we can fill out all the fields for our blog content, then hit the Save button.

After saving, Pages CMS will create the Markdown file, store the file in the proper directory, and automatically commit the changes to our repository. This is how Pages CMS helps us manage our content without needing to use git directly.

Automatically deploying

The only thing left to do is set up automated deployments through the service provider of your choice. Astro has integrations with providers like Netlify, Cloudflare Pages, and Vercel, but can be hosted anywhere you can run node applications.

Astro is typically very fast to build (thanks to Vite), so while site updates won’t be instant, they will still be fairly quick to deploy. If your site is set up to use Astro’s server-side rendering capabilities, rather than a completely static site, the changes might be much faster to deploy.

Wrapping up

Using a template as reference, we checked out how Astro content collections work alongside Pages CMS. We also learned how to connect our project repository to the Pages CMS app, and how to make content updates through the dashboard. Finally, if you are able, don’t forget to set up an automated deployment, so content publishes quickly.


Using Pages CMS for Static Site Content Management

Scroll to Top