komal.nakrani
Browse Themes
ThemesCategoriesDocsBlogTips & Tricks
CMS & Integrations••By Komal Nakrani

Choosing a Headless CMS for Astro

How to choose an Astro CMS: when Markdown in Git is enough, Git-based vs API-based CMS options, how loaders connect them, and rebuilding on publish.

#Astro#CMS#Headless CMS#Content Layer#Decap CMS

The right CMS for Astro depends on who edits the content. If developers write the content, Markdown files in Git with content collections need no CMS at all. If non-technical editors need a web interface but content can stay in the repository, use a Git-based CMS such as Decap CMS, Keystatic or TinaCMS. If content is shared across several sites or apps, or edited by a large team, use an API-based headless CMS such as Sanity, Storyblok, Contentful or Payload, and pull it into Astro with a content loader.

This guide explains each option, how it plugs into an Astro 7 site, and how to keep a static site updated when editors publish.

Key Takeaways

  • Start with Markdown and content collections. Add a CMS when someone who doesn’t use Git needs to edit.
  • Git-based CMSs commit Markdown to your repo, so the site stays a plain static build with no external dependency.
  • API-based CMSs store content in their own database. Astro fetches it at build time through a loader or fetch().
  • On a static site, connect the CMS’s publish webhook to your host’s deploy hook so new content triggers a rebuild.
  • Live collections and on-demand pages are for content that must update without a rebuild.

Do you need a CMS at all?

Many Astro sites don’t. Content collections give you typed Markdown and MDX with validation at build time. Editing is opening a file, writing, committing, and the host deploys. For a developer’s blog, a docs site maintained by engineers, or a company site updated a few times a month by a technical founder, that is the simplest and most durable setup.

You need a CMS when:

  • Editors don’t use Git. Marketing, clients, writers.
  • Editors need previews before publishing, in a browser.
  • Content is shared between a website, an app and other channels.
  • Volume is high: many authors, workflows, scheduled publishing, roles.
  • Assets are managed by non-developers, with uploads, cropping and alt text in a UI.

If only the first applies, a Git-based CMS is usually enough.

Two families: Git-based and API-based

Git-based CMS API-based headless CMS
Where content lives Markdown/JSON files in your repo The CMS provider’s database
How Astro reads it Content collections, as local files Loader or fetch() at build/request time
Publish flow CMS commits → host rebuilds CMS webhook → host rebuilds (or live fetch)
Content history Git history CMS revision history
Vendor dependency Low; files stay yours Higher; content is in their system
Best for Small teams, marketing sites, blogs Larger teams, multi-channel content
Examples Decap CMS, Keystatic, TinaCMS, Pages CMS Sanity, Storyblok, Contentful, Payload, Strapi, Hygraph

Some products straddle the line. TinaCMS, for example, stores content in Git but adds its own API layer for editing. Check how each one stores data, because that decides how hard it is to leave later.

Git-based CMS: content stays in your repo

A Git-based CMS is an admin interface that reads and writes files in your repository through the GitHub (or GitLab) API. Editors log in at /admin/, edit in a form, and the CMS commits a Markdown file. Your Astro site never knows a CMS exists; it reads files as usual.

With Decap CMS (formerly Netlify CMS), the setup is two static files in public/:

<!-- public/admin/index.html -->
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Content Manager</title>
    <script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
  </head>
  <body></body>
</html>
# public/admin/config.yml
backend:
  name: github
  repo: your-org/your-site
  branch: main
media_folder: public/images/blog
public_folder: /images/blog
collections:
  - name: blog
    label: Blog
    folder: src/content/blog
    create: true
    fields:
      - { name: title, label: Title, widget: string }
      - { name: description, label: Description, widget: text }
      - { name: pubDate, label: Publish date, widget: datetime }
      - { name: body, label: Body, widget: markdown }

Keep the CMS fields in sync with your collection schema in src/content.config.ts, so whatever editors save passes validation. Authentication needs an OAuth provider for GitHub; the Decap CMS docs list the options for each host.

Advantages: no database, no API bills, content versioned with code, and moving away from the CMS later means deleting two files. Limitations: one site per repo, basic workflows, and every publish is a commit and rebuild.

API-based headless CMS: content in a service

An API-based CMS stores content in its own backend and exposes it over a REST or GraphQL API. Astro pulls it in at build time, so the output is still a static site.

Pulling content with a loader

The cleanest integration in Astro 7 is a content loader, which makes CMS data look exactly like a local collection:

// src/content.config.ts
import { defineCollection } from 'astro:content';
import { z } from 'astro/zod';

const posts = defineCollection({
  loader: async () => {
    const res = await fetch('https://cms.example.com/api/posts?status=published', {
      headers: { Authorization: `Bearer ${import.meta.env.CMS_TOKEN}` },
    });
    const { items } = await res.json();
    return items.map((item: any) => ({
      id: item.slug,
      title: item.title,
      excerpt: item.excerpt,
      publishedAt: item.published_at,
      html: item.body_html,
    }));
  },
  schema: z.object({
    title: z.string(),
    excerpt: z.string(),
    publishedAt: z.coerce.date(),
    html: z.string(),
  }),
});

export const collections = { posts };

Pages then query it with getCollection('posts'), and the schema catches API changes at build time. Many CMS vendors and community authors publish ready-made loader packages; check the Astro integrations directory and the vendor’s docs before writing your own. The content loader reference covers object loaders with incremental updates for large sites.

The Astro content collections guide explains loaders, schemas and querying in full.

Rendering rich text

CMSs return rich text in different shapes: HTML strings, Markdown, or a structured JSON format (Portable Text, Storyblok’s rich text, Contentful’s rich text). HTML can be rendered with set:html if you trust the source. Structured formats need the vendor’s renderer, which usually has an Astro or framework-agnostic package. Sanitise any HTML that editors can write freely.

Visual editing and previews

Several API-based CMSs offer live visual editing, where editors click on the page to change it. This needs the page rendered on request or a preview build, which means an adapter and an on-demand preview route. Plan for it from the start if editors expect it, because it changes your deployment.

Rebuilding when content changes

A static site only shows new CMS content after a rebuild. Wire it up once:

  1. In your host (Cloudflare Pages, Netlify, Vercel), create a deploy hook. It’s a URL that triggers a build when called.
  2. In the CMS, add a webhook on publish/unpublish that calls the deploy hook URL.
  3. Publish a test entry and watch the build start.

For a typical marketing site or blog, a rebuild takes a minute or two, which editors find acceptable. If they don’t, you have two options:

  • On-demand pages (export const prerender = false) that fetch from the CMS on each request, cached at the CDN.
  • Live collections (defineLiveCollection() in src/live.config.ts) that fetch on request through the same collection-style API.

Both need an adapter. The Astro SSR guide explains the trade-offs, and the deploy guide covers hosts and adapters.

Themes built on content collections

Komal blog and editorial themes keep content in Markdown collections, so you can run them with no CMS, add a Git-based CMS, or swap the loader for an API. Browse blog and editorial themes.

Model your content before you pick a CMS

The most common CMS regret isn’t the vendor. It’s a content model that copies page layouts instead of describing content. Before signing up for anything, write down your content types and their fields.

A poor model mirrors the page:

HomePage
  heroTitle, heroText, section1Title, section1Body, section2Title, ...

Every redesign means a schema change, and nothing can be reused elsewhere.

A better model describes the things your business has:

Service      name, summary, body, icon, order
CaseStudy    title, client → Client, services → Service[], results[], cover
Client       name, logo, industry
Testimonial  quote, person, role, client → Client
Post         title, description, body, author → Author, tags[], pubDate

Pages then compose those types: the home page shows three featured case studies and two testimonials. Redesigning the home page changes the Astro template, not the content.

The same model maps directly onto Astro content collections with reference() fields, which makes it easy to start in Markdown and move to a CMS later without rewriting pages. The content collections guide has a worked case-study example.

Images from a CMS

Every API-based CMS hosts uploaded images, and most have an image CDN that resizes on request through URL parameters. You have two options:

Let Astro process them. Allow the domain and use <Image /> with the remote URL:

// astro.config.mjs
export default defineConfig({
  image: { remotePatterns: [{ protocol: 'https', hostname: '**.cms-cdn.example' }] },
});
<Image src={post.data.coverUrl} alt={post.data.coverAlt} width={1200} height={630} />

Astro downloads and optimises each image at build time. Builds get slower as image counts grow, but the output is self-contained.

Use the CMS’s image CDN. Build srcset URLs with the CMS’s resize parameters and render a plain <img>. Builds stay fast, and images are served from the CMS’s CDN. Many CMS SDKs include an image URL helper for this.

For large media libraries the second option usually wins. Either way, require alt text as a field in the CMS so editors can’t publish images without it.

Migrating content between systems

Content moves more often than people plan for: from Markdown to a CMS as a team grows, or from one CMS to another when pricing changes. A few habits make it painless:

  • Keep stable ids. Use slugs as ids in collections, so URLs don’t change when the backend does.
  • Keep the schema in Astro. Even with an API loader, a Zod schema in src/content.config.ts documents what pages expect. A new backend just has to produce that shape.
  • Store rich text portably. Markdown or HTML exports cleanly; proprietary block formats need a converter.
  • Script the migration. A one-off Node script that reads Markdown and writes to the CMS API (or the reverse) is usually an afternoon’s work when the model is clean.

Decision guide by team

Solo developer or technical founder. Markdown in Git with content collections. No CMS.

Small business, a few non-technical editors, one website. A Git-based CMS. Low cost, no lock-in, content stays in your repo.

Agency handing a site to a client. A Git-based CMS for simple sites; an API-based CMS with a generous free tier for clients who need media libraries, roles or visual editing. Check the licence and pricing your client will inherit.

Content team publishing daily across web and app. An API-based headless CMS with scheduled publishing, workflows and roles, connected through a loader.

Large catalogues or frequently changing data. An API-based CMS plus on-demand rendering or live collections for the volatile parts, static pages for the rest.

Questions to ask before choosing

  • Where does the content live, and can I export all of it in a standard format?
  • What do editors see? Ask for a demo with your own content model.
  • How does it authenticate editors, and who manages accounts?
  • What happens to cost as editors, entries, API calls and bandwidth grow?
  • Is there an Astro loader or a well-documented API?
  • Does preview or visual editing require on-demand rendering?

The Astro CMS guides list setup instructions for most major CMSs.

FAQ

What is the best CMS for Astro?

There isn’t one best option. For developer-edited sites, Markdown in Git needs no CMS. For non-technical editors on a single site, a Git-based CMS like Decap CMS or Keystatic is simplest. For larger teams or multi-channel content, an API-based CMS such as Sanity, Storyblok or Contentful fits better.

Can I use WordPress as a headless CMS with Astro?

Yes. WordPress exposes content through its REST API (and GraphQL with a plugin). Fetch posts in a loader or page frontmatter at build time, and trigger a rebuild on publish.

Does a headless CMS make my Astro site slower?

Not for visitors if content is fetched at build time; the output is still static HTML. It can make builds slower if you fetch a lot of content or images, which loaders with incremental updates help with.

How do I update my static Astro site when content changes in the CMS?

Create a deploy hook in your host and register it as a publish webhook in the CMS. Each publish triggers a rebuild and deploy.

Do I need SSR to use a CMS with Astro?

No. Static builds work with any CMS. You need on-demand rendering only for live previews, visual editing, or content that must update without a rebuild.