Skip to content

Instantly share code, notes, and snippets.

@hkulekci
Created December 26, 2025 07:04
Show Gist options
  • Select an option

  • Save hkulekci/83acc9a886094fde55b28763f18abd4b to your computer and use it in GitHub Desktop.

Select an option

Save hkulekci/83acc9a886094fde55b28763f18abd4b to your computer and use it in GitHub Desktop.
Comparison of Headless CMS

Headless CMS comparison for multi-tenant website builders

Payload CMS emerges as the clear winner for building a website platform serving thousands of tenants. Its native multi-tenancy plugin enables a single deployment to serve unlimited tenants efficiently, while most competitors either lack multi-tenancy entirely or become prohibitively expensive at scale. For teams prioritizing full control and cost efficiency, self-hosted Directus ranks second; Firebase/Firestore offers a viable path for those willing to build custom CMS features on top of proven infrastructure.

The fundamental challenge is that most headless CMS solutions were designed for single-tenant use cases. Strapi, Ghost, and even enterprise platforms like Contentful hit architectural walls when scaled to thousands of separate tenant websites. The comparison below reveals which platforms can actually handle this demanding use case.

Multi-tenancy is the decisive factor

The website builder use case fundamentally requires true multi-tenancy—serving thousands of isolated tenant websites from shared infrastructure. This requirement immediately eliminates several popular options.

Payload CMS delivers native multi-tenancy through its official @payloadcms/plugin-multi-tenant plugin. A single codebase, single database, and single deployment can serve unlimited tenants with complete data isolation. The plugin automatically adds tenant fields to collections, enforces role-based access control (super-admins vs. tenant users), and supports custom domains per tenant. One agency reports successfully running 500+ tenants on this architecture. Three isolation strategies are available: user-based (shared login), path-based (URL routing), and domain-based (hostname detection).

Strapi has no native multi-tenancy support—a critical finding confirmed in official documentation. The recommended architecture is "one Strapi instance per tenant," meaning thousands of separate Node.js processes and databases. A community plugin (strapi-plugin-multi-tenant) provides workarounds, but Strapi explicitly states true multi-tenancy is not implemented as of v5. This effectively disqualifies Strapi for the website builder use case without massive custom infrastructure investment.

CMS Multi-tenancy approach Tenant limit Practical for 1000s?
Payload CMS Native plugin, single deployment Unlimited ✅ Yes
Directus RBAC + tenant_id filtering (self-hosted) Unlimited ✅ Yes (with effort)
Firebase/Firestore Collection-based isolation + Security Rules Unlimited ✅ Yes (custom build)
Sanity Datasets OR document filtering 4 datasets (Growth), custom (Enterprise) ⚠️ Enterprise required
Contentful Spaces 750 max per organization ❌ Not viable
Hygraph Enumeration + roles (Enterprise only) Enterprise required ⚠️ Very expensive
Strapi Separate instances required N/A ❌ Not viable
Ghost Separate instances required N/A ❌ Not viable

Sanity and Contentful have structural limits that prevent thousands-of-tenants scale. Sanity's Growth plan allows only 4 datasets maximum ($999/month per additional dataset), forcing either Enterprise pricing or a complex single-dataset approach with document-level filtering. Contentful caps organizations at 750 spaces, and space pricing ranges from $8,000 to $75,000+ annually per space—making multi-space architectures financially impossible at scale.

Pricing models diverge dramatically at scale

Self-hosted open-source solutions offer order-of-magnitude cost savings compared to managed platforms when serving thousands of tenants.

Payload CMS pricing is the most favorable for this use case:

  • Self-hosted core is free forever (MIT license) with no seat limits, no entry limits
  • Payload Cloud had tiers from $35-$199/month but new sign-ups are currently paused following Figma acquisition
  • Estimated infrastructure cost for thousands of tenants: $500-2,000/month (single deployment scaling with database/storage, not per-tenant)

Strapi Cloud pricing demonstrates why multi-instance approaches fail at scale:

  • Essential tier: $15-18/project/month (50,000 API requests)
  • Pro tier: $99/project/month
  • 1,000 tenants × $15-99 = $15,000-99,000/month just for CMS hosting

Firebase/Firestore offers predictable usage-based pricing:

  • Free tier: 50,000 reads/day, 20,000 writes/day, 1GB storage
  • Blaze plan: $0.06 per 100,000 reads, $0.18 per 100,000 writes
  • Realistic estimate for 1,000 tenant sites with moderate traffic: $250-500/month
  • Hidden costs: international egress ($0.12-0.23/GB), Cloud Functions compute, Security Rules evaluation counting as reads

Managed CMS enterprise pricing becomes prohibitive:

Platform Enterprise starting price Notes
Sanity $60,000-100,000+/year Custom datasets, RBAC at scale
Contentful $60,000+/year (list), ~$37,620 (negotiated median) Per-space costs additional
Hygraph $15,000-50,000+/year estimated Multi-tenancy Enterprise-only

Directus self-hosted offers a middle path:

  • Free for organizations under $5M annual revenue
  • Commercial license: ~$1,200-6,000/year (Vendr data average: $3,600/year)
  • Directus Cloud: $15-99/month per project (not viable for thousands)

Performance and scalability vary significantly

CDN caching strategy becomes critical for all platforms—both Sanity and Contentful offer unlimited cached requests from their CDNs, meaning proper cache configuration determines actual costs and performance.

Payload CMS performance benefits from its TypeScript-first architecture, claiming 40-60% faster response times than traditional CMS. Native Next.js integration enables ISR/SSG patterns. Horizontal scaling works naturally—one deployment, multiple serverless instances via Vercel or Cloudflare.

Directus performance requires careful optimization:

  • Simple queries: 40-72ms with caching
  • Complex queries (15 relations + permissions): ~1.5s for 10 req/sec without cache
  • Permission evaluation adds ~30% overhead in benchmarks
  • Node.js single-threaded architecture requires horizontal scaling via Redis for high concurrency

Firebase/Firestore latency varies considerably:

  • Simple document read: 50-200ms
  • Complex query: 200-500ms
  • Real-time listener roundtrip: 600-1,500ms
  • Cold Cloud Function + Firestore: 5-12 seconds (major gotcha)
  • Mitigation: Keep 1-5 minimum instances warm, use 2nd-gen functions with concurrency up to 1,000

Rate limits comparison (uncached API requests):

Platform Rate limit Notes
Sanity 25 mutations/sec; CDN unlimited API CDN requests unlimited for cached
Contentful Free: 55/s, Paid: 78/s (per space) CMA: 7-10/s
Hygraph Hobby: 5/s, Growth: 25/s, Enterprise: 500/s Cached unlimited
Firebase ~1 write/sec per document sustained 500/50/5 warmup rule for new collections

Strapi's known performance issue (GitHub #13288): queries with populate=* on complex relations can spike to 9-20 seconds with 3,500+ entries—a significant concern for content-heavy tenant sites.

Localization capabilities differ substantially

For multi-language website support, the platforms split between those with native i18n and those requiring custom implementation.

Native i18n leaders:

  • Sanity: Unlimited locales included (all plans), but attribute limits constrain practical usage (10,000 attributes on Growth)
  • Strapi v5: Built-in field-level localization, 500+ pre-created locales, AI translation on Growth plan
  • Payload CMS: Field-level OR document-level localization, automatic fallback handling, 30+ admin UI languages, no locale limits
  • Hygraph: Native per-field localization with independent publishing, but 2-3 locales on lower tiers; 8+ locales require Enterprise

Contentful's locale structure provides flexibility but tier-locks features:

  • Free: 2 locales
  • Lite: 3 locales
  • Premium: Up to 500 per environment
  • Fallback chains configurable (e.g., es-MX → es-ES → en-US)

Firebase requires manual implementation:

  • No built-in i18n—must structure documents/collections manually
  • Pattern options: locale-based collections or embedded translations within documents
  • Firebase Hosting supports i18n rewrites based on Accept-Language header
  • Translate Text Extension available (Cloud Translation API, ~$20 per 1M characters)

Ghost's i18n is notably weak:

  • Single-language sites only
  • No multi-language content from one installation
  • Admin interface not internationalized
  • Official workaround: separate Ghost instances per language

API flexibility for dynamic content and forms

The website builder use case demands flexible APIs for menus, forms, and dynamic UI components.

Best API flexibility:

  • Payload CMS: REST + GraphQL native, Local API (direct database access without HTTP overhead), official @payloadcms/plugin-form-builder for native form handling, 2-4 webhooks (more on Enterprise)
  • Sanity: GROQ query language enables powerful projections, joins across references in single queries, GraphQL supported, 2-4 webhooks by plan
  • Directus: REST + GraphQL + Flows automation system, visual workflow builder for webhooks, 9 extension types for custom functionality

GraphQL-native options:

  • Hygraph: GraphQL-only (no REST), real-time subscriptions, union types for flexible content modeling, content federation connecting external APIs
  • Contentful: Full REST APIs (CDA, CMA, CPA) plus GraphQL, Sync API for delta updates, cross-space references on Premium

Firebase's webhook limitation: No native webhooks—must implement via Cloud Functions triggers, adding complexity and cold-start latency concerns.

Form handling comparison:

Platform Native forms Approach
Payload CMS ✅ Official plugin Form builder with submissions collection
Directus Model as collection + M2A field components
Sanity Custom content types + external service
Contentful Custom content types + external service
Firebase Cloud Functions + custom collection

Self-hosted vs managed determines long-term costs

For thousands of tenants, self-hosting becomes economically necessary.

Fully self-hostable (recommended for this use case):

Platform Requirements Notes
Payload CMS Node.js 20+, MongoDB/PostgreSQL/SQLite 1-click deploy to Vercel/Cloudflare, serverless-friendly
Directus Node.js 18+, PostgreSQL/MySQL/SQLite/etc Docker recommended, Redis required for horizontal scaling
Strapi Node.js 18+, PostgreSQL/MySQL Per-tenant instances make this impractical
Ghost Node.js 20, MySQL 8 (production) Single-node only, multi-server clustering unsupported

Managed-only platforms:

  • Sanity: Studio can be self-hosted (React app), but Content Lake is managed—queries always go to Sanity's cloud
  • Contentful: Fully managed, no self-hosting option
  • Hygraph: Fully managed, no self-hosting option
  • Builder.io: SaaS only, no self-hosting

Firebase positioning: Google-managed infrastructure with no self-hosting, but usage-based pricing makes it more predictable than per-seat/per-space models. Data portability concerns exist due to proprietary query syntax.

Directus self-hosting advantages include full database access (work with existing schemas), no artificial limits on users/entries/API calls, and database-agnostic support (PostgreSQL, MySQL, MariaDB, SQLite, MS-SQL, CockroachDB, OracleDB).

Development effort and ecosystem maturity

Setup complexity and community support significantly impact time-to-production.

Lowest development effort for multi-tenant setup:

Platform Setup time Multi-tenant complexity Documentation
Payload CMS ~10-20 min Low (native plugin) Excellent
Firebase ~30 min (basic) Medium (custom rules/structure) Excellent
Directus ~15-30 min Medium (RBAC setup) Good
Sanity ~15-30 min High (Enterprise features) Good
Strapi ~15-30 min Very High (no native support) Comprehensive

Community and ecosystem size:

Platform GitHub stars Plugin ecosystem Learning resources
Strapi 70,800+ 150+ marketplace plugins Extensive
Directus 33,000+ Vue-based extensions Growing
Payload CMS 32,000+ Smaller but high-quality official plugins Excellent docs
Sanity N/A 500+ plugins/tools Good
Contentful N/A ~100 marketplace apps Comprehensive

TypeScript support (critical for large codebases):

  • Payload CMS: TypeScript-first with full type generation
  • Strapi: Supported but not TypeScript-first
  • Directus: Good TypeScript SDK, composable architecture
  • Sanity: Excellent TypeScript support

Admin UI considerations:

  • Firebase has no admin UI out of box—requires FireCMS, Rowy, or custom build (4-8 weeks for basic CMS features)
  • Directus and Payload provide polished admin UIs
  • Sanity Studio is highly customizable but code-based schema increases initial setup time

Content modeling flexibility for website components

Website builders need flexible schemas for menus, page blocks, forms, and dynamic components.

Page builder patterns:

Payload CMS excels here with blocks that can nest within blocks (with maxDepth prevention for infinite recursion), @payloadcms/plugin-nested-docs for parent/child hierarchies, and full React component customization for admin UI.

Strapi's Dynamic Zones provide reusable components but have a critical limitation: Dynamic Zones cannot nest inside components (GitHub #5798, still unimplemented). This restricts complex page builder layouts.

Directus uses Many-to-Any (M2A) relationships for component-based pages—each component type is a separate collection with ordering preserved. This requires more manual setup but offers full flexibility.

Hygraph provides Union Types (GraphQL-native) perfect for varied block types, plus nestable Components—but model limits apply (40 models on Growth, 500 on Enterprise).

Content type limits comparison:

Platform Content types Fields per type Records
Payload CMS Unlimited Unlimited Unlimited (self-hosted)
Directus Unlimited Unlimited Unlimited (self-hosted)
Sanity Unlimited Attribute limits apply 10M per project
Contentful Free: 25, Premium: 1,000 50 per type 5M per space
Hygraph Hobby: 20, Growth: 40 Varies Growth: 10,000 entries
Strapi Unlimited Varies Depends on infrastructure

Rich text flexibility:

  • Sanity's Portable Text: JSON-based, fully extensible, custom blocks allowed—can embed forms, videos, callouts directly in rich text
  • Contentful Rich Text: Proprietary format, limited to linked entries (cannot add arbitrary custom blocks)
  • Payload CMS: Lexical editor with custom block support

Critical gotchas by platform

Payload CMS gotchas:

  • Payload Cloud sign-ups currently paused (Figma acquisition)—self-hosting required
  • MongoDB is the first-class database; PostgreSQL support via adapter is newer
  • Smaller plugin ecosystem than Strapi
  • Code-first approach less accessible for non-developers

Strapi gotchas:

  • Multi-tenancy requires thousands of separate instances—operationally untenable
  • populate=* queries can take 9-20 seconds with complex relations
  • Dynamic Zones cannot nest inside components
  • Linear cost scaling with tenant count

Firebase/Firestore gotchas:

  • No admin UI—significant development investment required
  • Cold start delays of 5-12 seconds for Cloud Functions
  • Query limitations: only one inequality filter per query, no JOINs, no full-text search without Algolia/Elasticsearch
  • Security Rules complexity scales with multi-tenant requirements
  • 30+ minutes offline triggers full re-query charges on reconnect

Sanity gotchas:

  • Only 4 datasets maximum on Growth plan ($999/month each additional)
  • Attribute limits (10,000 on Growth) can be hit with many content types × locales
  • Per-seat pricing escalates with many tenant admins
  • Enterprise required for custom roles/RBAC at scale

Contentful gotchas:

  • 750 spaces maximum per organization—mathematically insufficient for thousands of tenants
  • Space costs are $8,000-75,000+ annually per space
  • 25 content types limit on starter spaces
  • Premium required for cross-space references

Directus gotchas:

  • Permission evaluation adds ~30% latency overhead
  • No automatic indexes on foreign keys (must create manually)
  • $5M revenue threshold triggers commercial license requirement
  • Single-threaded Node.js requires horizontal scaling for high concurrency

Conclusion

For a multi-tenant website builder serving thousands of tenants, the recommendation hierarchy is:

  1. Payload CMS (self-hosted) — Native multi-tenancy plugin designed exactly for this architecture, single deployment efficiency, excellent TypeScript support, and nested blocks for flexible page builders. The paused Cloud service means self-hosting is required, but Vercel/Cloudflare 1-click deploys minimize operational burden.

  2. Directus (self-hosted) — Best alternative if PostgreSQL preference or database-first approach is required. Full database control enables optimization at scale, but requires manual RBAC setup for tenant isolation and careful indexing strategy.

  3. Firebase/Firestore — Viable for teams comfortable building custom CMS features. Usage-based pricing scales predictably, real-time capabilities are excellent, but requires FireCMS/Rowy or custom admin development. Best when paired with existing Firebase/GCP infrastructure.

  4. Sanity (Enterprise) — Worth evaluating if managed infrastructure is non-negotiable and budget allows $60,000+/year. Single-project document filtering can work at scale with proper access control, but requires Enterprise for the necessary RBAC features.

Platforms to avoid for this use case:

  • Strapi — No native multi-tenancy makes it fundamentally unsuited for thousands of tenants
  • Ghost — Blog-focused architecture with no custom content types or multi-tenancy
  • Contentful — Space limits (750 max) and per-space pricing make it economically impossible
  • Hygraph — Enterprise-only multi-tenancy with managed-only deployment; entry limits constrain scale
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment