Node.js CMS in 2026: Payload, Strapi, Directus, Keystone, and Sanity compared
By Nayan Kyada · · 7 min read
Part of The Sanity + Next.js Guide
If you're building a custom website on Node.js and need a CMS to match, five names keep coming up: Payload, Strapi, Directus, Keystone, and Sanity. They're all JavaScript-native, all TypeScript-friendly, and all capable of feeding a Next.js frontend — but they make very different bets on where complexity lives and who pays for it.
What "Node.js CMS" actually means here
I'm using the term loosely to mean: a CMS whose core runtime is Node.js, whose config or schema is written in JS/TS, and which a JavaScript developer can extend without leaving the language. That rules out Contentful, Storyblok, and Hygraph (all closed-source SaaS platforms with no meaningful Node.js extension layer). It includes Sanity even though the content lake is hosted — the Studio, the schema, the GROQ client, and the document actions are all TypeScript running in Node.
The five options at a glance
| Payload | Strapi | Directus | Keystone | Sanity | |
|---|---|---|---|---|---|
| License | MIT | SEE / Community | BSL 1.1 | MIT | Proprietary |
| Self-host option | Yes (required on free) | Yes | Yes | Yes | No |
| Managed cloud | Payload Cloud ($25+/mo) | Strapi Cloud ($29+/mo) | Directus Cloud ($15+/mo) | No official cloud | Sanity Cloud (free tier) |
| Schema language | TypeScript config | JS/TS files or GUI | Admin GUI + YAML/JSON | TypeScript config | TypeScript config |
| DB layer | Postgres / MongoDB / SQLite | Postgres / MySQL / SQLite | Postgres / MySQL / SQLite / Mongo | Postgres / SQLite | Managed (proprietary) |
| REST + GraphQL | Both | Both | REST only (GraphQL plugin) | GraphQL only | GROQ + REST |
| Real-time / live preview | Via Next.js draft mode | Via Strapi preview | Via Directus Realtime | Limited | Sanity Presentation |
| Image handling | Basic transforms | Cloudinary/custom plugin | Built-in transforms | None | Sanity CDN + hotspot |
| TypeScript depth | Deep (schema → types) | Shallow | Shallow | Deep (schema → types) | Deep (TypeGen) |
| Rough infra cost (small project) | $0 self-host + VPS ~$12/mo | $0 self-host + VPS ~$12/mo | $0 self-host + VPS ~$12/mo | $0 self-host + VPS ~$12/mo | $0 (free tier) |
Payload CMS
Payload v3 is my current default recommendation for teams that want full ownership and are comfortable writing schema as TypeScript. The config file is pure TS, collections generate types automatically, and the v3 integration with Next.js App Router means you can run the CMS and the frontend in the same Next.js project — no separate server.
The trade-off is operational. You own the database, the backups, the upgrades. On a $12/mo VPS that's fine for a small marketing site. For a high-availability production app you need managed Postgres, proper backups, and monitoring — suddenly "free" costs real engineering time. Payload Cloud starts at $25/mo and handles that for you, but it's still early and doesn't have the track record of a fully managed SaaS.
// payload.config.ts — minimal collection with typed fields
import { buildConfig } from 'payload/config'
import { postgresAdapter } from '@payloadcms/db-postgres'
export default buildConfig({
db: postgresAdapter({
pool: { connectionString: process.env.DATABASE_URL },
}),
collections: [
{
slug: 'posts',
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'body', type: 'richText' },
{ name: 'publishedAt', type: 'date' },
],
},
],
})Strapi
Strapi is the most widely deployed Node.js CMS by install count. The GUI-based content type builder lowers the entry bar for non-developers, which is genuinely useful when a client needs to add a field without filing a ticket.
The problems I've hit in production: the TypeScript support is bolted on rather than intrinsic (types are generated, not inferred from config), the plugin ecosystem is fragile across major versions, and the BSL-adjacent licensing on Strapi v5's enterprise features can catch teams off guard. For a Next.js project where a developer controls the schema anyway, I don't reach for Strapi first — the GUI builder buys nothing and the type safety gap is annoying.
Directus
Directus takes a different angle: it wraps an existing database and generates a REST API and admin UI from the schema rather than owning the schema itself. That's genuinely powerful for teams migrating an existing Postgres database or building data-heavy internal tools. The Flows feature (visual automation) is also surprisingly capable.
For a greenfield content website it's overkill. The BSL 1.1 license also means you can't offer Directus as a managed service to clients without a commercial agreement. Worth knowing before you propose it.
Keystone
Keystone is the underdog here and it deserves more attention in TypeScript-first teams. The schema is pure TypeScript with deep GraphQL integration — you define a list, Keystone generates the GraphQL schema, the Admin UI, and the TypeScript types in one step. It's maintained by Thinkmill and has been production-stable for years.
The gaps: no official managed cloud, image handling is DIY, and the community is small compared to Strapi or Payload. I'd reach for Keystone on an internal tool or a project where GraphQL is a hard requirement and the team is comfortable managing their own infra.
// keystone.ts — Post list with auto-generated types and GraphQL
import { config, list } from '@keystone-6/core'
import { text, timestamp, select } from '@keystone-6/core/fields'
export default config({
db: { provider: 'postgresql', url: process.env.DATABASE_URL! },
lists: {
Post: list({
fields: {
title: text({ validation: { isRequired: true } }),
status: select({ options: ['draft', 'published'], defaultValue: 'draft' }),
publishedAt: timestamp(),
},
}),
},
})Sanity
Sanity is the outlier in this list because you can't self-host the content lake. The Studio and schema are Node.js/TypeScript, but the data lives on Sanity's infrastructure. For some teams that's disqualifying; for most product and agency teams it's the right trade-off — you get a fast global CDN, real-time collaboration, Presentation (live preview), image transforms with hotspot, and a free tier that covers most small projects.
Typegen generates TypeScript interfaces directly from GROQ queries, which is the tightest end-to-end type safety of any option here. The free tier (up to 2 users, 200k API requests/month, 10 GB bandwidth) is genuinely usable in production for a marketing site. Growth plan starts at $15/seat/month.
The constraint worth calling out: if you need custom server-side business logic (e.g., a webhook that writes back to the CMS on an order event), you're adding a separate API layer. With Payload or Keystone that logic lives inside the CMS config.
Which one to pick
Pick Payload if you want full ownership, are running Next.js App Router, and your team is comfortable with TypeScript config and managing a database. Best self-hosted Node.js CMS right now.
Pick Strapi if a non-developer needs to manage the schema through a GUI and you're willing to accept looser TypeScript coverage.
Pick Directus if you're wrapping an existing database or building a data-heavy internal dashboard, not a content-first website.
Pick Keystone if GraphQL is a hard requirement and you want the cleanest TypeScript-to-schema mapping with full infrastructure ownership.
Pick Sanity if you want zero infrastructure management, real-time collaboration, best-in-class image delivery, and you're comfortable trading self-host control for a managed SaaS. It's what I use on most client projects for exactly those reasons.
The honest answer: for a custom Next.js marketing site in 2026, it's almost always Payload or Sanity. Payload when the client needs to own every byte. Sanity when speed of delivery and editorial experience matter more than infrastructure control.