Why Headless CMS?
A headless CMS separates content management from presentation. Marketing edits content in a UI; your app fetches it via API and renders it however it wants.
This matters for:
- Marketing pages that change weekly without deploys
- Blog content managed by non-developers
- Multi-channel content (web, mobile, email)
Contentful: The Enterprise Default
npm install contentful
import { createClient } from 'contentful';
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID!,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
});
// Fetch entries
const entries = await client.getEntries<BlogPost>({
content_type: 'blogPost',
order: '-sys.createdAt',
limit: 10,
'fields.status': 'published',
});
const posts = entries.items.map(entry => ({
id: entry.sys.id,
title: entry.fields.title,
slug: entry.fields.slug,
body: documentToHtmlString(entry.fields.body), // rich text → HTML
coverImage: entry.fields.coverImage?.fields.file.url,
}));
// Next.js with ISR
export async function getStaticProps({ params }) {
const entry = await client.getEntries({
content_type: 'blogPost',
'fields.slug': params.slug,
});
return {
props: { post: entry.items[0] },
revalidate: 60, // re-fetch every 60 seconds
};
}
Pricing: Free: 1 space, 25k API calls/month. Team: $489/month.
Strengths: Mature, great dashboard, CDN-backed delivery API, strong ecosystem.
Limitations: Expensive at scale, content modeling is rigid.
Sanity: Flexible and Developer-First
npm install next-sanity @sanity/image-url
import { createClient } from 'next-sanity';
const client = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: 'production',
apiVersion: '2024-01-01',
useCdn: true,
});
// GROQ query language (like GraphQL but simpler)
const posts = await client.fetch(`
*[_type == "post" && status == "published"] | order(publishedAt desc) [0..9] {
_id,
title,
"slug": slug.current,
publishedAt,
"author": author->{ name, "image": image.asset->url },
"coverImage": mainImage.asset->url,
excerpt
}
`);
// Real-time updates with live preview
import { useLiveQuery } from 'next-sanity/preview';
function BlogPage() {
const [posts] = useLiveQuery(initialPosts, POSTS_QUERY);
return <PostList posts={posts} />;
}
Pricing: Free: 3 users, 10GB storage. Growth: $15/user/month.
Strengths: Extremely flexible content modeling (GROQ is powerful), real-time collaboration, customizable Studio UI, great for complex content structures.
Limitations: Steeper learning curve (GROQ is unique), Studio customization requires React knowledge.
Strapi: Open Source, Self-Hosted
npx create-strapi-app my-cms --quickstart
Strapi generates a REST and GraphQL API automatically from your content types.
// Fetch from your self-hosted Strapi
const response = await fetch(
`${process.env.STRAPI_URL}/api/posts?populate=*&sort=createdAt:desc&pagination[limit]=10`,
{ headers: { Authorization: `Bearer ${process.env.STRAPI_TOKEN}` } }
);
const { data } = await response.json();
// Or use the SDK
import Strapi from '@strapi/sdk-js';
const strapi = new Strapi({ baseURL: process.env.STRAPI_URL! });
const { data: posts } = await strapi.find('posts', {
populate: ['coverImage', 'author'],
sort: ['createdAt:desc'],
pagination: { limit: 10 },
});
Pricing: Open source (self-host free). Cloud: $29/month.
Strengths: Full control, no vendor lock-in, customizable with plugins.
Limitations: You manage the infrastructure, upgrades can be painful.
Decision Guide
| Scenario | Recommendation |
|---|---|
| Marketing site, non-dev editors | Contentful or Sanity |
| Complex content relationships | Sanity |
| Budget-conscious, technical team | Strapi (self-hosted) |
| Enterprise with compliance needs | Contentful Enterprise |
| Rapid prototyping | Strapi Cloud or Sanity free |
| Need real-time preview | Sanity |
The Practical Advice
For a new SaaS:
- Start with Sanity (generous free tier, great DX)
- Or use MDX files in your repo if content changes rarely
- Migrate to a proper CMS when marketing needs to edit without deploying
Don't over-engineer. Static MDX in a content/ folder is often the right answer until you have a marketing team.
Blog with Sanity CMS integration and Next.js ISR: Whoff Agents AI SaaS Starter Kit.
Top comments (0)