Built for developers, loved by editors
Every feature you need to build, edit, and deliver content — all running on your own infrastructure with full Sanity compatibility.
Sanity-Compatible Schemas
Define your content models with the same defineType and defineField helpers you already know from Sanity. Migrate existing schemas with zero changes — CellCMS reads them natively and generates the underlying PostgreSQL tables automatically.
- Full defineType / defineField / defineArrayMember API
- Validation rules, custom input components, and fieldsets
- Automatic PostgreSQL table and index generation
- Schema diffing with safe migrations on deploy
import { defineType, defineField } from '@cellcms/schemas'
export const post = defineType({
name: 'post',
title: 'Blog Post',
type: 'document',
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
validation: (rule) => rule.required().max(120),
}),
defineField({
name: 'slug',
title: 'Slug',
type: 'slug',
options: { source: 'title' },
}),
defineField({
name: 'author',
title: 'Author',
type: 'reference',
to: [{ type: 'author' }],
}),
defineField({
name: 'body',
title: 'Body',
type: 'blockContent',
}),
],
})GROQ Query Engine
CellCMS ships a full GROQ parser and query planner that compiles every GROQ expression into optimised PostgreSQL. You get the expressive, JSON-native query language you love — backed by a battle-tested relational database.
- Complete GROQ specification support including pipes, projections, and joins
- Custom parser with AST-level optimisations
- Compiled to parameterised PostgreSQL — no N+1 queries
- Query explain endpoint for debugging slow queries
// Fetch featured posts with nested author + category data
const featured = await client.fetch(`
*[_type == "post" && featured == true]
| order(publishedAt desc) [0...6] {
_id,
title,
"slug": slug.current,
publishedAt,
excerpt,
"author": author-> {
name,
"avatar": image.asset->url
},
"categories": categories[]-> {
title,
"slug": slug.current
},
"cover": mainImage.asset->url
+ "?w=800&h=450&fit=crop"
}
`)Content Studio
A beautiful three-panel editor that feels instantly familiar. The document list, editor form, and live preview sit side-by-side so editors can see exactly how their changes will look — before publishing.
- Three-panel layout: list, editor, and live preview
- Draft / publish workflow with one-click publishing
- Scheduled publishing with cron-based release queues
- Full revision history with visual diffs and one-click rollback
- Real-time presence indicators so you never overwrite a colleague
- Customisable with plugins, field-level components, and dashboard widgets
Content Studio
Three-panel layout with list, editor, and live preview panes — drag to resize, customise per document type.
Drop-in Client SDK
The @cellcms/client package is API-compatible with @sanity/client. Change one import line and your existing front-end code — queries, projections, mutations — just works.
- Same createClient() initialisation API
- fetch(), create(), patch(), delete() methods
- Typed responses with generics
- Built-in CDN-aware caching and stale-while-revalidate
// Before (Sanity)
// import { createClient } from '@sanity/client'
// After (CellCMS) — everything else stays the same
import { createClient } from '@cellcms/client'
export const client = createClient({
projectId: 'my-project',
dataset: 'production',
apiVersion: '2026-03-01',
useCdn: true,
})
// Your existing queries work unchanged
export async function getPosts() {
return client.fetch(`
*[_type == "post"]
| order(publishedAt desc) [0...10]
`)
}Asset Pipeline
Upload once, serve everywhere. CellCMS processes images on-the-fly with Sharp — resize, crop, convert to WebP or AVIF, and apply focal-point cropping — all through simple URL parameters.
- On-the-fly transforms: resize, crop, format, quality
- Focal-point aware cropping from the Studio UI
- Storage backends: local filesystem or S3-compatible (AWS, R2, MinIO)
- Automatic srcset generation for responsive images
<!-- Original upload -->
<img src="/assets/hero.jpg" />
<!-- Resized to 800px wide, WebP, 80% quality -->
<img src="/assets/hero.jpg?w=800&fm=webp&q=80" />
<!-- 400x400 crop around the focal point -->
<img src="/assets/hero.jpg?w=400&h=400&fit=crop&fp-x=0.4&fp-y=0.3" />
<!-- Responsive srcset generated automatically -->
<img
src="/assets/hero.jpg?w=800&fm=webp"
srcset="
/assets/hero.jpg?w=400&fm=webp 400w,
/assets/hero.jpg?w=800&fm=webp 800w,
/assets/hero.jpg?w=1200&fm=webp 1200w
"
sizes="(max-width: 600px) 400px, 800px"
/>Real-time Collaboration
WebSocket-powered presence and live queries keep every client in sync. See who is editing a document, get instant updates when content changes, and build reactive front-ends with zero polling.
- Document-level presence indicators with avatar stacking
- Field-level locking to prevent conflicting edits
- Live queries — subscribe to a GROQ query and receive patches
- WebSocket transport with automatic reconnection and heartbeat
Real-time Collaboration
Real-time presence with avatar stacking, field-level locking, and live query subscriptions that push patches instantly.
Webhooks with HMAC Signing
Trigger external builds, sync third-party services, or run custom workflows every time content changes. Every webhook payload is signed with HMAC-SHA256 so you can verify authenticity on the receiving end.
- HMAC-SHA256 signed payloads for secure verification
- Configurable per document type and action (create, update, delete, publish)
- Exponential back-off retry with dead-letter queue
- Vercel / Netlify / Cloudflare build trigger presets
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'node:crypto'
const SECRET = process.env.CELLCMS_WEBHOOK_SECRET!
export async function POST(req: NextRequest) {
const body = await req.text()
const signature = req.headers.get('x-cellcms-signature')
// Verify HMAC-SHA256 signature
const expected = crypto
.createHmac('sha256', SECRET)
.update(body)
.digest('hex')
if (signature !== expected) {
return NextResponse.json(
{ error: 'Invalid signature' },
{ status: 401 }
)
}
const payload = JSON.parse(body)
// Trigger ISR revalidation, send notification, etc.
console.log('Webhook received:', payload)
return NextResponse.json({ ok: true })
}Instant Cloud Deployment
Sign up and start building in minutes. CellCMS handles the infrastructure — PostgreSQL, asset storage, CDN, and scaling — so you can focus on your content and frontend.
- Zero infrastructure to manage
- Automatic scaling and high availability
- Configurable via environment variables
- Health-check endpoints for monitoring
import { createClient } from '@cellcms/client'
const client = createClient({
apiUrl: 'https://api.cellcms.com',
project: 'my-project',
dataset: 'production',
token: process.env.CELLCMS_TOKEN,
})
// Fetch content — that's it. No infra to manage.
const posts = await client.fetch(
'*[_type == "post"] | order(publishedAt desc) [0...10]'
)See it in action
Spin up a local instance in under two minutes and explore every feature first-hand.