Payload CMS

TypeScript-native headless CMS and application framework

Content Management Systems ★ 44.9k stars Medium setup MIT

Payload is an open-source headless CMS and application framework built fully in TypeScript and React. It generates a powerful admin UI and APIs from code-first configuration, integrating tightly with Next.js.

Key features

  • Code-first configuration
  • Auto-generated admin UI
  • Native Next.js integration
  • REST, GraphQL, and local APIs

Pros & cons

Strengths

  • Excellent TypeScript developer experience
  • No vendor lock-in

Trade-offs

  • Requires development skills
  • Younger ecosystem

Payload CMS replaces

Last reviewed Aug 26, 2026 · 874 words

Payload is a CMS you install with npx create-payload-app, configure by writing TypeScript, and deploy as a Next.js application, and if any of those three phrases put you off, Directus or WordPress will make you happier. For a developer, the trade is excellent: define a collection in about 15 lines of code and Payload generates the database tables, the admin UI, the REST and GraphQL APIs, and the TypeScript types, with no hosted service in the loop and an MIT licence.

Configuration is code, and that is the entire pitch

Everything about a Payload site lives in payload.config.ts and the files it imports. A blog posts collection looks like this:

import type { CollectionConfig } from 'payload'

export const Posts: CollectionConfig = {
  slug: 'posts',
  admin: { useAsTitle: 'title' },
  access: { read: () => true },
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'content', type: 'richText' },
    { name: 'publishedAt', type: 'date' },
    { name: 'author', type: 'relationship', relationTo: 'users' },
  ],
}

Save that, and the admin panel at /admin has a Posts section with a Lexical rich-text editor, the REST API answers at /api/posts, GraphQL at /api/graphql, and payload generate:types writes a Post interface for your frontend. Access control is a function per operation, hooks run before and after every change, and versions with drafts are one config flag. Because the config is a git-tracked file, the schema of a Payload site is code-reviewed and deployed like the rest of the application, which is the thing Contentful and Sanity users pay to approximate with migration scripts.

It lives inside Next.js, so hosting means hosting Next.js

Since version 3, Payload installs into a Next.js app under /app/(payload), and the admin UI, the APIs, and your public site are one process. That has a Local API consequence that is easy to undervalue: server components can call payload.find({ collection: 'posts' }) directly, no HTTP, no serialisation, typed results. It also has a hosting consequence: you deploy a Node process, not a static site plus a separate CMS. The template's Dockerfile produces a standalone Next.js build; the container wants about 512 MB of RAM at idle and a reverse proxy in front of port 3000. Two environment variables are mandatory: PAYLOAD_SECRET (a long random string used to sign auth tokens; changing it logs everyone out) and DATABASE_URI.

Postgres, MongoDB, or SQLite: choose once

Payload's database adapters are real, first-class, and different. The Postgres adapter (@payloadcms/db-postgres) uses Drizzle and gives you proper migrations via payload migrate, with a dev-mode push that alters the schema automatically as you edit collections. The MongoDB adapter was the original and is the most battle-tested, but a schemaless store under a strongly typed CMS always felt backwards to me. SQLite works and is fine for a personal site. I default to Postgres for the reasons the Postgres for everything post lays out, plus one Payload-specific reason: relationship queries there are real SQL joins rather than application-side lookups. Switching adapters later means exporting and re-importing content, so pick before you have data.

Where it beats the alternatives, and where it doesn't

NeedPayloadStrapiDirectus
Schema defined inTypeScript codeAdmin UI, saved to JSON filesExisting database tables
Non-developer can add a fieldNoYesYes
Typed API for the frontendGenerated, first-classCommunity toolingGenerated SDK
Renders your site tooYes, it is a Next.js appNo, headless onlyNo, headless only
Media storageLocal disk or S3 adapterLocal or upload providersLocal or S3-compatible

The "younger ecosystem" con in the catalogue is accurate. There are fewer plugins than Strapi, fewer tutorials, and the Next.js coupling means a Vue or SvelteKit frontend consumes Payload only over HTTP, losing the Local API. Rich-text output is Lexical JSON, not HTML, so your frontend needs a serialiser (Payload ships one for React). And a content editor who wants to add a "subtitle" field cannot do it without a developer, a commit, and a deploy. That is a feature for a product team and a bug for an agency handing off to a client.

Backups are two things, not one

Content is in the database; uploaded media is on disk under the collection's staticDir or in your S3 bucket. Back up both, and back up the git repo, because a database restore without the matching payload.config.ts is a table of rows nobody can render. I run pg_dump nightly and point the S3 storage adapter at a MinIO bucket that has its own replication, which keeps the app container fully stateless and trivial to redeploy.

What I'd do

If you are a TypeScript developer building a site or app that needs a CMS, use Payload with the Postgres adapter, deploy it as one container behind Caddy, put media in S3 or MinIO, and treat payload.config.ts as the source of truth for everything. If a non-technical person must own the content model, or the frontend is not React, choose Directus. Payload is the first headless CMS where I did not miss the hosted version, and code-first config is why.

Compare Payload CMS

7 head-to-head comparisons.

Similar content management systems apps