Skip to content

CMS Decision Tree

Use this tree every time a client needs to edit their own content. Default is always Content Collections (agency writes all content).

Does the client need to edit content themselves?
├── No
│ └── Content Collections (MDX in Git, zero cost, best performance)
├── Yes, 1–3 editors, comfortable with an admin UI
│ └── Keystatic (free up to 3 users, Git-native, Astro-first)
├── Yes, non-technical editor, needs true visual editing
│ └── CloudCannon ($49/month, official Astro partner Mar 2026)
├── Yes, medium / multi-user, structured content APIs
│ └── Sanity (best DX, generous free tier, real-time collab)
├── Yes, non-technical marketing team, visual + component editing
│ └── Storyblok (#1 Enterprise Headless CMS G2 2025, 582% ROI)
└── Yes, enterprise / multi-brand / multi-channel
└── Contentful
CMSBest forAstro integrationFree tierPaid
Content CollectionsAgency controls all contentNative (no CMS needed)Free forever
Keystatic1–3 technical editors, Git workflowOfficial, first-class3 users free$10/month Pro
CloudCannonNon-technical visual editingOfficial Astro partner (Mar 2026)No$49/month Standard
SanityMulti-user, structured contentOfficial3 users, 2 datasets$15/user/month
StoryblokNon-technical marketing teams, component editingOfficial1 user$99/month Grow
ContentfulEnterprise, multi-brand, multi-channelOfficial5 users, limited$300+/month
  • Requires output: 'hybrid' (already our default — needed for /api/contact)
  • GitHub App setup is automated (no manual OAuth config)
  • Admin UI at /keystatic — editors never see Git or terminal
  • Stores content as Markdoc/MDX files directly in src/content/docs/
  • Official Astro CMS partner as of March 12, 2026
  • Connects to same GitHub repo — Vercel still builds and deploys (no lock-in)
  • Client Sharing: password-protected /update/ URL — client never needs a CloudCannon account
  • Client Organizations: client pays CloudCannon directly — agency not subsidizing
  • Standard plan: $49/month, unlimited sites, 3 users (+$10/user)
  • Every edit creates a Git commit (can clutter history — use squash merges)

For most clients, the agency writes all content. Use Content Collections:

src/content/blog/
├── post-slug.mdx
└── another-post.mdx
src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
image: z.string().optional(),
}),
});
export const collections = { blog };
  • Zero CMS cost
  • Best SEO performance (statically generated)
  • Content travels with the repo on client handover
  • Agency publishes via GitHub API (from Boxi CRM) or manual commit