DEV Community

Atlas Whoff
Atlas Whoff

Posted on

Headless CMS Comparison: Contentful vs Sanity vs Strapi for Developers

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
Enter fullscreen mode Exit fullscreen mode
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,
}));
Enter fullscreen mode Exit fullscreen mode
// 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
  };
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
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} />;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
// 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 },
});
Enter fullscreen mode Exit fullscreen mode

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:

  1. Start with Sanity (generous free tier, great DX)
  2. Or use MDX files in your repo if content changes rarely
  3. 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)