Features

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.

Schemas

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
schemas/post.ts
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',
    }),
  ],
})
Queries

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
GROQ query
// 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"
  }
`)
Editing

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.

Integration

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
lib/cms.ts
// 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]
  `)
}
Media

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
URL transform example
<!-- 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"
/>
Collaboration

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.

Automation

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
api/webhook/route.ts
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 })
}
Deployment

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
app/page.tsx
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.