The Best Headless CMS Options for Astro Sites in 2026
We have used four different content management approaches across our Astro projects. The Threshline blog you are reading right now uses Astro Content Collections with local Markdown files. Client projects have used Sanity, Contentful, and Keystatic. We briefly evaluated Strapi for a project that ended up going with Sanity instead.
None of these is the best CMS. Each is the best CMS for a specific situation. This post breaks down when to use what, based on our actual experience shipping with each.
Astro Content Collections: The Default Choice
If you are building a developer-managed site — a blog, documentation site, portfolio, or marketing page where the people editing content are comfortable with Markdown and Git — Content Collections is the obvious choice. It is built into Astro, requires zero external services, costs nothing, and gives you type-safe content with Zod schemas.
Here is how we define the blog collection for this site:
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
author: z.string(),
tags: z.array(z.string()),
draft: z.boolean().optional().default(false),
}),
});
export const collections = { blog };
Content lives as Markdown files in src/content/blog/. We get frontmatter validation at build time — if someone adds a post without a required field, the build fails. No runtime surprises.
Querying content is straightforward:
// src/pages/blog/[slug].astro
import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => {
return data.draft !== true;
});
const sortedPosts = posts.sort(
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
);
When to use Content Collections:
- Your content editors are developers or comfortable with Git
- You want zero external dependencies
- You want type-safe content with build-time validation
- Your site is statically generated
- You do not need a visual editing experience
When to avoid it:
- Non-technical editors need to manage content
- You need real-time content updates without rebuilding
- Your content model is complex with relations and assets
We wrote about our overall Astro setup in our post on why we chose Astro for our website. Content Collections was a big part of that decision.

Sanity: The Power Tool
Sanity is what we reach for when a project needs a proper editorial experience. It gives you a fully customizable content studio, real-time collaboration, and a powerful query language (GROQ) that can express complex content relationships.
The Sanity + Astro integration is mature. Here is a typical setup:
// src/lib/sanity.ts
import { createClient } from '@sanity/client';
export const sanityClient = createClient({
projectId: import.meta.env.SANITY_PROJECT_ID,
dataset: 'production',
apiVersion: '2026-01-01',
useCdn: true,
});
export async function getPosts() {
return sanityClient.fetch(`
*[_type == "post" && !(_id in path("drafts.**"))] | order(publishedAt desc) {
title,
slug,
publishedAt,
excerpt,
"author": author->{ name, image },
"categories": categories[]->{ title, slug },
mainImage {
asset->{ url, metadata { dimensions } }
}
}
`);
}
GROQ takes some getting used to, but it is powerful. You can resolve references, project specific fields, and filter — all in a single query. Once you learn it, you do not want to go back to REST endpoints.
The Sanity Studio is a React app that you can embed in your own site or host separately. We usually embed it at /admin during development and host it on Sanity’s managed platform for production.
What we like about Sanity:
- The content modeling is incredibly flexible. Custom object types, portable text (their rich text format), and references between documents.
- Real-time preview with Astro’s server-side rendering or their Presentation tool.
- The free tier is generous — 100K API requests/month, 1M assets, 10GB bandwidth.
- GROQ is genuinely a better query language for content than GraphQL.
What we do not like:
- The learning curve is steep. Setting up schemas, configuring the studio, and learning GROQ takes time.
- Image handling requires using their image URL builder for transformations. It works well but is another API to learn.
- The studio customization API changes frequently. We have had to update studio configurations during major version bumps.
When we evaluated content infrastructure for SpotsMexico — a location directory with rich content, images, and geographic data — Sanity was the right call. The content model was complex enough to justify the setup cost, and the editorial team needed a visual interface.
Contentful: The Enterprise Option
Contentful is the most established headless CMS. It has been around since 2013, has excellent documentation, and integrates with everything. If you are building for a client who already uses Contentful, or you need an enterprise-grade CMS with roles, workflows, and compliance features, it is a solid choice.
The Astro integration uses their JavaScript SDK:
// src/lib/contentful.ts
import contentful from 'contentful';
const client = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.CONTENTFUL_ACCESS_TOKEN,
});
export async function getBlogPosts() {
const entries = await client.getEntries({
content_type: 'blogPost',
order: ['-fields.publishedDate'],
include: 2, // resolve 2 levels of linked entries
});
return entries.items.map((item) => ({
title: item.fields.title as string,
slug: item.fields.slug as string,
body: item.fields.body, // Rich text document
publishedDate: item.fields.publishedDate as string,
author: item.fields.author as { fields: { name: string } },
}));
}
What Contentful does well:
- The editorial interface is polished and intuitive. Non-technical editors can use it without training.
- Content modeling through the web UI is drag-and-drop friendly.
- The webhook system is reliable for triggering Astro rebuilds.
- Localization support is built in and comprehensive.
The downsides:
- Pricing. The free tier gives you 1 environment, 25K records, and 2 million API calls. That sounds generous until you realize each asset and each locale counts as a separate record. For a content-heavy site, you hit limits fast.
- The API is REST-based and requires multiple requests for deeply nested content. Their GraphQL API helps but adds another layer.
- Rich text rendering requires a dedicated renderer package, and the output can be verbose.
// Rendering Contentful rich text in Astro
import { documentToHtmlString } from '@contentful/rich-text-html-renderer';
import { BLOCKS, INLINES } from '@contentful/rich-text-types';
const renderOptions = {
renderNode: {
[BLOCKS.EMBEDDED_ASSET]: (node) => {
const { url, description } = node.data.target.fields.file;
return `<img src="${url}" alt="${description || ''}" loading="lazy" />`;
},
[INLINES.HYPERLINK]: (node, next) => {
return `<a href="${node.data.uri}" target="_blank" rel="noopener">${next(node.content)}</a>`;
},
},
};
const htmlContent = documentToHtmlString(post.body, renderOptions);
We tend to recommend Contentful when a client specifically requests it or when the project requires enterprise features like content approval workflows, scheduled publishing, and granular role-based access.

Keystatic: The Middle Ground
Keystatic is the newer option that fills an interesting gap. It gives you a visual editing UI that writes directly to your repository — either local files (like Content Collections) or via a GitHub integration. You get the editorial experience of a headless CMS without the external service dependency.
// keystatic.config.ts
import { config, collection, fields } from '@keystatic/core';
export default config({
storage: {
kind: 'local', // or 'github' for collaborative editing
},
collections: {
posts: collection({
label: 'Blog Posts',
slugField: 'title',
path: 'src/content/blog/*',
format: { contentField: 'content' },
schema: {
title: fields.slug({ name: { label: 'Title' } }),
description: fields.text({ label: 'Description' }),
pubDate: fields.date({ label: 'Publish Date' }),
content: fields.markdoc({ label: 'Content' }),
},
}),
},
});
Keystatic gives non-technical editors a WYSIWYG-like interface while keeping content in your repository as Markdown or Markdoc files. Changes go through Git, which means you get version history, pull request reviews, and your existing deployment pipeline.
What makes Keystatic compelling:
- No external service. Content stays in your repository.
- The editing experience is significantly better than raw Markdown for non-developers.
- It works with Astro Content Collections, so you can start with Markdown and add Keystatic later without changing your content structure.
- The GitHub storage mode enables collaborative editing through the browser without running anything locally.
The limitations:
- It is relatively new. The ecosystem and community are smaller than Sanity or Contentful.
- Media management is basic. You are storing images in your repository or configuring external storage.
- The editing UI, while good, is not as feature-rich as Sanity Studio or Contentful’s interface.
We have used Keystatic on a client project where the content team had two people — a developer and a marketing coordinator. The developer worked in Markdown directly, and the coordinator used the Keystatic UI. It worked well for that size of team.
Strapi: The Self-Hosted Option
Strapi is open-source and self-hostable. If you need full control over your data and infrastructure, or your client has requirements about where data lives, Strapi is the main contender.
We evaluated Strapi for a project with data residency requirements. The content needed to stay on infrastructure the client controlled, which ruled out Sanity and Contentful. Strapi let us deploy the CMS on their existing cloud infrastructure.
// src/lib/strapi.ts
const STRAPI_URL = import.meta.env.STRAPI_URL;
const STRAPI_TOKEN = import.meta.env.STRAPI_API_TOKEN;
export async function getPosts() {
const response = await fetch(
`${STRAPI_URL}/api/posts?populate=*&sort=publishedAt:desc`,
{
headers: {
Authorization: `Bearer ${STRAPI_TOKEN}`,
},
}
);
const { data } = await response.json();
return data.map((item: any) => ({
id: item.id,
title: item.attributes.title,
slug: item.attributes.slug,
content: item.attributes.content,
publishedAt: item.attributes.publishedAt,
}));
}
Strapi’s strengths:
- Self-hosted. Your data, your servers.
- The admin panel is decent and auto-generated from your content types.
- REST and GraphQL APIs are both available.
- Plugin ecosystem for extending functionality.
The trade-offs:
- You are running and maintaining a Node.js server. That means hosting costs, updates, backups, and security patches.
- The developer experience is rougher than Sanity or Contentful. Schema changes require careful migration handling.
- Performance under load requires proper infrastructure — caching layers, CDN configuration, database tuning.
We ended up recommending Sanity for most projects where we initially considered Strapi. Unless data residency or self-hosting is a hard requirement, the operational overhead of Strapi is rarely worth it.

Comparison Table
| Feature | Content Collections | Sanity | Contentful | Keystatic | Strapi |
|---|---|---|---|---|---|
| Cost (small project) | Free | Free | Free | Free | Free (self-hosted) |
| Cost (scaled) | Free | $99+/mo | $300+/mo | Free | Hosting costs |
| Editorial UI | None (Markdown) | Excellent | Excellent | Good | Good |
| Developer experience | Excellent | Great | Good | Great | Decent |
| Real-time preview | No | Yes | Yes | Local only | Possible |
| Self-hosted | N/A (files) | No | No | Yes (files) | Yes |
| Astro integration | Built-in | SDK | SDK | Native | API |
| Learning curve | Low | High | Medium | Low | Medium |
Our Decision Framework
When a new project comes in, here is how we decide:
- Who is editing content? If it is developers only, Content Collections. If non-technical editors are involved, we need a CMS with a UI.
- How complex is the content model? Simple blog posts and pages can work with any option. Complex models with relations, variants, and media — Sanity or Contentful.
- What is the budget? Zero budget means Content Collections or Keystatic. If there is CMS budget, Sanity’s free tier is generous for small projects.
- Are there data residency requirements? If content must live on specific infrastructure, Strapi or Keystatic (with Git storage).
- How often does content change? Daily updates benefit from a real CMS with a proper editorial workflow. Monthly updates are fine with Markdown and Git.
For the majority of Astro sites we build, Content Collections is the starting point. It is simple, it is fast, and it removes an entire external dependency from your stack. We only add a headless CMS when the project genuinely needs one — and we are honest about that distinction.
If you are building an Astro site and trying to figure out the right content strategy, reach out at [email protected]. We have been through this decision enough times to help you skip the wrong turns.