Setting Up Pages CMS for Jekyll
My blog has been running on Jekyll and GitHub Pages for close to a decade now.
The workflow has always been the same: write markdown in vim, commit, push.
That works great when I’m at my computer, but not so much when I want to jot
something down from my phone or when I move around from computer to computer.
Pages CMS solves that.
It’s a free and open-source CMS that sits on top of your GitHub repo and gives you
a web-based editor for your content.
Getting Started
Setup is straightforward. Go to app.pagescms.org,
sign in with GitHub, and authorize it to access your repo. Then add a
.pages.yml file to the root of your repo to configure it.
The Config
Here’s the config I ended up with after quite a bit of trial and error:
media:
- name: images
input: images/posts
output: /images/posts
extensions: [png, jpg, webp]
content:
- name: posts
label: Posts
type: collection
path: _posts
format: yaml-frontmatter
filename: "{year}/{year}-{month}-{day}-{fields.title}.md"
media: images
view:
fields: [title, date]
default:
sort: date
order: desc
fields:
- name: title
label: Title
type: string
required: true
- name: date
label: Date
type: date
required: true
- name: tags
label: Tags
type: select
list: true
required: false
options:
values:
- Pens
- Pi Project
- name: categories
label: Categories
type: select
list: true
required: false
options:
values:
- .NET
- Jekyll
- GitHub Pages
# ... full list of your categories
- name: body
label: Content
type: rich-text
options:
media: images
Pitfalls
Of course nothing is easy. So here are some of the issues that I learned along the way.
type: list Does Not Exist
This was the biggest one. I originally had my tags and categories configured as:
- name: tags
type: list
This silently fails. Pages CMS’s valid field types are string, number,
date, boolean, select, rich-text, text, code, image, file,
object, and a few others. There is no list type.
To make a field repeatable (an array), you use the list property on any
field type:
- name: tags
type: string
list: true
Or, if you want a dropdown with predefined values:
- name: tags
type: select
list: true
options:
values: [Pens, Arduino, Recipes]
The really frustrating part is that type: list doesn’t produce a config error.
The field just silently doesn’t work. If you combine it with hidden: true
(say, to auto-tag posts), you get a validation error on save
(“Please fix the errors before saving”) with no indication of what’s wrong
because the broken field is hidden.
path: "." Doesn’t Work for Collections
I tried to set up a “Pages” collection to edit files in the repo root
(index.html, about/index.md, etc.):
- name: pages
type: collection
path: .
Pages CMS doesn’t accept . as a path. The fix is to use type: file entries
for individual files instead:
- name: about
label: About Page
type: file
path: about/index.md
format: yaml-frontmatter
Subdirectory-Organized Posts Show as Folders
If your posts are organized in year subdirectories (_posts/2024/,
_posts/2025/, etc.), Pages CMS displays them as a folder tree rather than a
flat sorted list. I tried view.layout: list but it didn’t help. This
appears to be a limitation since the folder view is driven by the filesystem
structure.
It’s annoying to have a blog list posts in chronological order, but it’s still worlds better than what I had before.
Hidden Fields with Bad Types Break Silently
I tried creating a separate “Project: Pens” collection that would auto-tag
posts. This didn’t work as expected which is why I wound up going with the
dropdown (type: select) on the main posts collection instead.
Bonus: Enforcing Tag Consistency
After a decade of free-form typing, my categories were a mess, e.g., c# vs C#,
docker vs Docker, tools vs Tools. Using type: select with an explicit
list of values fixes this going forward. No more typos, no more casing
inconsistencies.