DEV Community

Joshua Matthews
Joshua Matthews

Posted on

Choosing a Headless CMS: A Practical Comparison for 2025

The headless CMS market is crowded. Sanity, Contentful, Strapi, Payload, Directus, Hygraph, Builder.io - the list keeps growing.

After implementing dozens of CMS projects, here's how to actually choose.

The Decision Framework

Before comparing features, answer these questions:

  1. Who's editing content? Technical team or marketing/content folks?
  2. How complex is your content model? Blog posts or interconnected product data?
  3. What's your budget? Free tier limits or enterprise pricing?
  4. Self-hosted or managed? Control vs convenience trade-off
  5. How important is the editing experience? Real-time preview? Visual builder?

Your answers narrow the field significantly.

The Contenders

Sanity

Best for: Complex content models, real-time collaboration, developer flexibility

Pricing: Generous free tier (10K documents, 500K API requests/month). Growth plans start around $99/month.

Standout features:

  • GROQ query language - incredibly powerful
  • Real-time collaborative editing
  • Portable Text for rich content
  • Customisable Studio (React-based)
// Sanity schema example
export default {
  name: 'product',
  type: 'document',
  fields: [
    { name: 'title', type: 'string' },
    { name: 'slug', type: 'slug', options: { source: 'title' } },
    { name: 'description', type: 'portableText' },
    { name: 'price', type: 'number' },
    { 
      name: 'categories', 
      type: 'array',
      of: [{ type: 'reference', to: [{ type: 'category' }] }]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Downsides: Learning curve for GROQ. Studio customisation requires React knowledge.

Contentful

Best for: Enterprise teams, established workflows, extensive integrations

Pricing: Free tier limited (25K records). Paid plans from $300/month - expensive.

Standout features:

  • Mature platform, battle-tested
  • Excellent internationalisation
  • Rich integration ecosystem
  • Good for large editorial teams

Downsides: Pricing scales quickly. Less flexible than Sanity for custom fields.

Strapi

Best for: Developers who want full control, self-hosted preference, budget-conscious projects

Pricing: Free and open source. Cloud hosting from $29/month.

Standout features:

  • Self-hostable (you own your data)
  • Auto-generated REST and GraphQL APIs
  • Plugin system for extensibility
  • Content types built in admin UI
// Strapi content type (created via admin or code)
// api/product/content-types/product/schema.json
{
  "kind": "collectionType",
  "attributes": {
    "title": { "type": "string", "required": true },
    "description": { "type": "richtext" },
    "price": { "type": "decimal" },
    "images": { "type": "media", "multiple": true }
  }
}
Enter fullscreen mode Exit fullscreen mode

Downsides: Self-hosting means you manage infrastructure. Less polished editing experience than hosted options.

Payload CMS

Best for: Developers who want code-first config, TypeScript projects, full ownership

Pricing: Free and open source. Cloud coming.

Standout features:

  • 100% TypeScript, code-first
  • Built-in authentication and access control
  • Excellent developer experience
  • Growing rapidly
// Payload collection config
import { CollectionConfig } from 'payload/types';

const Products: CollectionConfig = {
  slug: 'products',
  admin: { useAsTitle: 'title' },
  access: {
    read: () => true,
    create: ({ req }) => req.user?.role === 'admin'
  },
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'price', type: 'number' },
    { name: 'description', type: 'richText' },
    { 
      name: 'category', 
      type: 'relationship', 
      relationTo: 'categories' 
    }
  ]
};
Enter fullscreen mode Exit fullscreen mode

Downsides: Younger ecosystem. Self-hosted only (for now). Smaller community than Strapi.

Builder.io

Best for: Visual editing, marketing teams, non-technical content creators

Pricing: Free tier available. Paid from $19/month.

Standout features:

  • Visual drag-and-drop builder
  • Integrates with existing React/Next.js components
  • A/B testing built-in
  • No developer bottleneck for landing pages

Downsides: Less suitable for structured data. Best for visual content, not complex data models.

Quick Comparison Table

CMS Self-Hosted Visual Editor Free Tier Best For
Sanity No Studio (customisable) Generous Complex content, developers
Contentful No Yes Limited Enterprise, large teams
Strapi Yes Admin UI Unlimited Budget, full control
Payload Yes Admin UI Unlimited TypeScript projects
Builder.io No Visual builder Yes Marketing pages

Integration with Next.js

All modern headless CMSs work with Next.js. Here's the typical pattern:

// lib/cms.js - Sanity example
import { createClient } from '@sanity/client';const client = createClient({
  projectId: process.env.SANITY_PROJECT_ID,
  dataset: 'production',
  apiVersion: '2024-01-01',
  useCdn: true
});

export async function getProducts() {
  return client.fetch(`*[_type == "product"] {
    _id,
    title,
    "slug": slug.current,
    price,
    "imageUrl": image.asset->url
  }`);
}

// app/products/page.jsx
export default async function ProductsPage() {
  const products = await getProducts();

  return (
    <div>
      {products.map(product => (
        <ProductCard key={product._id} {...product} />
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Preview and Draft Mode

Most CMSs support draft previews. Here's the pattern:

// app/api/preview/route.js
import { draftMode } from 'next/headers';
import { redirect } from 'next/navigation';

export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const secret = searchParams.get('secret');
  const slug = searchParams.get('slug');

  if (secret !== process.env.PREVIEW_SECRET) {
    return new Response('Invalid token', { status: 401 });
  }

  draftMode().enable();
  redirect(`/products/${slug}`);
}

// In your data fetching
export async function getProduct(slug, preview = false) {
  const query = preview
    ? `*[_type == "product" && slug.current == $slug][0]`  // Include drafts
    : `*[_type == "product" && slug.current == $slug && !(_id in path("drafts.**"))][0]`;

  return client.fetch(query, { slug });
}
Enter fullscreen mode Exit fullscreen mode

My Recommendations

Just starting out? Go with Sanity. The free tier is generous, and it scales well.

Need full control? Strapi or Payload, self-hosted. You own everything.

Marketing-heavy site? Builder.io for landing pages, combined with Sanity for structured content.

Enterprise with budget? Contentful - mature, well-supported, integrates with everything.

TypeScript purist? Payload. Best developer experience in the code-first space.

The Real Answer

The "best" CMS is the one your content editors will actually use.

Involve them in the decision. Let them try the editing experience. A technically superior CMS is worthless if your marketing team hates using it.


We've implemented headless CMS solutions for hotels, e-commerce, and content-heavy sites at LogicLeap. Happy to help you choose the right one.

Top comments (1)

Collapse
 
mattatdirectus profile image
MattatDirectus • Edited

Directus mentioned at the top, but not in the contender list? πŸ€”πŸ˜­
Anything we can do to rank in the contenders, in your opinion?

Based on your boxes -
Self-Hosted βœ…
Visual Editor βœ…
Free Tier Unlimited + all features for orgs under $5m annual revenue/funding (license required for above)
Best For CMS+, meaning your website + more since it is essentially a BaaS with an automation builder, visualizations, and advanced RBAC.

So just curious how we could get better! Thank you!