EmDash CMS vs Sanity: Which One Should You Choose?

EmDash CMS vs Sanity: Which One Should You Choose?

Sanity and EmDash both start from the same premise — content should be structured data, not raw HTML — but they built very different platforms around that idea. Sanity's focus is real-time collaborative editing and a content lake other applications can query directly. EmDash's focus is plugin security and AI-native automation on top of structured content. Both are legitimate answers to the same underlying problem.

Table of Contents
  1. Quick Answer
  2. Structured Content, Two Different Philosophies
  3. Real-Time Collaboration
  4. Pricing Model
  5. Plugin and Extension Security
  6. AI-Native Design
  7. Where Sanity Pulls Ahead
  8. Where EmDash Pulls Ahead
  9. The Bottom Line

Quick Answer

Sanity is the stronger choice if real-time multi-editor collaboration and a flexible, application-agnostic content lake are your priority. EmDash is the stronger choice if you want sandboxed plugin security, a tight Astro integration, and AI-native tooling built into the platform rather than added through Sanity Studio customization.

Structured Content, Two Different Philosophies

Sanity positions itself as a "Content Operating System" — content as structured, reusable data that can power automation, AI workflows, and purpose-built applications beyond just a website. It's genuinely flexible: teams build custom Sanity Studio configurations rather than working inside a fixed admin UI. EmDash's content model is also structured — portable text JSON with a dedicated, typed database table per content type — but it ships with a more opinionated, ready-to-use admin panel rather than a fully customizable studio you configure yourself.

Read also:

Real-Time Collaboration

This is one of Sanity's most distinctive features: multiple editors can work on the same document simultaneously, with presence indicators and conflict-free merging — closer to editing a Google Doc than filling out a CMS form. EmDash doesn't currently offer real-time multi-editor collaboration at that level; its editorial workflow is closer to the traditional single-editor-at-a-time model most CMS platforms use.

Pricing Model

Sanity's Free tier covers up to 20 users, 10,000 documents, and generous API request quotas, with the Growth plan starting around $15 per occupied seat per month once a team needs more datasets, roles, or scheduled publishing. Enterprise pricing is custom. EmDash is open-source and self-hosted, so its cost structure isn't per-seat at all — you pay for your own infrastructure rather than for each additional editor, which tends to favor larger content teams over Sanity's seat-based model as headcount grows.

Plugin and Extension Security

Sanity Studio's extensibility comes from React-based plugins and custom input components running inside the Studio application itself. EmDash takes a different approach at the platform level: plugins run in sandboxed, isolated worker environments and must explicitly declare the permissions they need, similar to OAuth scopes, rather than running with the same access as the rest of the admin application.

AI-Native Design

Sanity offers AI Assist as part of its Growth and higher plans, primarily for content generation and editing assistance inside Sanity Studio. EmDash's AI integration runs deeper architecturally — every installation ships with a built-in Model Context Protocol (MCP) server, so an AI agent can create content types, manage entries, and handle deployment programmatically as a native part of the system, not just generate copy inside an editing panel. See our full EmDash overview for more on how that's built.

Where Sanity Pulls Ahead

  • Real-time, multi-editor collaborative editing — genuinely differentiated in this category.
  • A highly customizable Studio for teams that want to build a fully bespoke editorial experience.
  • A mature, application-agnostic content lake for teams querying content from multiple products, not just one website.

Where EmDash Pulls Ahead

  • No per-seat pricing — costs scale with infrastructure, not editor headcount.
  • Sandboxed, permission-scoped plugin architecture baked into the platform.
  • A built-in MCP server for AI-native, programmatic content management out of the box.
  • Tighter native integration if your front end is already Astro.

The Bottom Line

If your team needs true real-time collaborative editing or wants to build a fully custom editorial experience on top of a flexible content lake, Sanity remains one of the most capable platforms in the composable CMS category. If per-seat pricing is a concern as your team grows, or plugin security and AI-native tooling matter more than Studio customization, EmDash is the closer fit — especially for teams already building on Astro.

Share

Comments

Write a comment

Related Articles

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

July 16, 2026

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

July 16, 2026

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

July 16, 2026

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

TL;DR — A custom session-cookie login flow appeared to succeed on localhost (the OTP verified, the response looked fine) but every subsequent request to a login-gated page treated the visitor as logged out. Identical code worked fine on the live HTTPS site. The cookie's Secure attribute was hardcoded to true — and per the cookie spec, browsers never store or send a Secure cookie over a plain, non-HTTPS connection.

Table of Contents
  1. Diagnostic
  2. Root cause
  3. Fix
  4. Lessons learned

Diagnostic

Check the actual Set-Cookie response header and the browser's own cookie storage panel — on localhost over http://, the cookie is sent by the server but never actually stored by the browser.

Root cause

// before -- assumes the app is always served over HTTPS
setCookie("session", token, { secure: true, httpOnly: true });
emdashkits.com

A cookie config that quietly assumes "we're always on HTTPS" breaks the instant you test over plain HTTP, which local dev servers commonly are.

Read also:

Fix

// after -- derive secure from the actual request protocol
const isHttps = request.url.startsWith("https://");
setCookie("session", token, { secure: isHttps, httpOnly: true });
emdashkits.com

Lessons learned

  • Any Secure-flagged cookie needs to key off the real request scheme, not an assumption baked in once at cookie-creation time.
  • "Works in production, silently fails in local dev" is a strong signal to check cookie flags before anything else in an auth flow.
  • Check other cookies in the same codebase for the same hardcoded assumption — if one cookie has this bug, sibling cookies set the same way are worth auditing too.
Share
Previous Article

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

Comments

Write a comment

Related Articles

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

July 16, 2026

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

July 16, 2026

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

Uploaded Images Disappear After Every Deploy on Shared Hosting (and Other Reverse-Proxy Gotchas)

July 16, 2026

Uploaded Images Disappear After Every Deploy on Shared Hosting (and Other Reverse-Proxy Gotchas)

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

TL;DR — GA4's gtag.js snippet was installed correctly, the page loaded with no visible JavaScript error, and GA4's real-time report still showed zero activity. The CMS's default Content-Security-Policy had no allowance for analytics domains and no config option to add one — so every request to Google's tracking endpoints was blocked at the browser level before it could fail loudly.

Table of Contents
  1. Diagnostic
  2. Root cause
  3. Lessons learned

Diagnostic

Check the browser's dedicated CSP violation reporting, not the regular console error list — CSP blocks are reported through their own channel, not thrown as normal script errors, so "no console errors" doesn't mean nothing was blocked.

Root cause

The CSP's script-src and connect-src directives had no entry for googletagmanager.com or google-analytics.com, and the CMS exposed no configuration surface to add one — the only way in was patching the CSP directives directly.

// patch-package: add analytics domains to the existing CSP directives
scriptSrc.push("https://www.googletagmanager.com");
connectSrc.push("https://www.google-analytics.com", "https://www.googletagmanager.com");
emdashkits.com
Read also:

Lessons learned

  • "No console errors" is not proof nothing was blocked — CSP violations live in their own reporting surface and are easy to miss if you're only scanning for red error text.
  • Before adding any third-party script tag to a site with a CSP already in place, check the CSP's directives first rather than assuming a silently-empty analytics dashboard means a snippet-installation mistake.
Share
Previous Article

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

Next Article

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

Comments

Write a comment

Related Articles

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

July 16, 2026

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

July 16, 2026

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

Uploaded Images Disappear After Every Deploy on Shared Hosting (and Other Reverse-Proxy Gotchas)

July 16, 2026

Uploaded Images Disappear After Every Deploy on Shared Hosting (and Other Reverse-Proxy Gotchas)