Home/CMS & Content/Payload CMS
CMS
payload-cms

Payload CMS

Headless CMSOpen SourceTypeScriptSelf-hostedNext.js

Open-source headless CMS built with TypeScript and React. Define your schema in code, get a fully-typed API and admin panel automatically. Self-hostable with zero vendor lock-in — runs inside your Next.js app.

License

MIT

Language

TypeScript

52
Trust
Limited

Why Payload CMS?

TypeScript project needing a code-first, self-hosted CMS

Running the CMS inside your Next.js app (Payload v3 is a Next.js plugin)

Avoiding vendor lock-in and monthly CMS fees

Signal Breakdown

What drives the Trust Score

Weekly npm downloads
65k/wk
GitHub commits (90d)
520
GitHub stars
32k
Stack Overflow questions
1.5k
Community health
Very Active
Weighted Trust Score52 / 100

Download Trend

Last 12 months

Tradeoffs & Caveats

Know before you commit

Non-technical content team needing a polished editor — Contentful or Sanity have a better editorial UX

Fully hosted with zero ops — Payload requires a server/database

Need real-time collaborative editing — Sanity's Studio is best-in-class here

Pricing

Free tier & paid plans

Free tier

Open source — self-hosted free forever

Paid

Payload Cloud from $19/mo

MIT license — no per-seat or API call fees ever

Alternative Tools

Other options worth considering

contentful
Contentful84Strong

The leading headless CMS for enterprise content teams. API-first content infrastructure with a powerful content modeling system, webhooks, and SDKs for any frontend framework.

sanity
Sanity86Strong

The structured content platform with a real-time collaborative editing studio (Sanity Studio). Best-in-class content modeling, GROQ query language, and live preview integrations.

Often Used Together

Complementary tools that pair well with Payload CMS

nextjs

Next.js

Frontend & UI

98Excellent
View
supabase

Supabase

Database & Cache

95Excellent
View
clerk

Clerk

Auth & Users

80Strong
View
vercel

Vercel

Hosting & Deploy

89Strong
View
cloudinary

Cloudinary

File & Media

86Strong
View

Learning Resources

Docs, videos, tutorials, and courses

Get Started

Repository and installation options

View on GitHub

github.com/payloadcms/payload

npm (create)npx create-payload-app@latest
npm (add to existing)npm install payload @payloadcms/richtext-lexical

Quick Start

Copy and adapt to get going fast

// Create a Payload v3 Next.js project
// npx create-payload-app@latest

// payload.config.ts
import { buildConfig } from 'payload';
import { postgresAdapter } from '@payloadcms/db-postgres';
import { lexicalEditor } from '@payloadcms/richtext-lexical';

export default buildConfig({
  secret: process.env.PAYLOAD_SECRET!,
  db: postgresAdapter({ pool: { connectionString: process.env.DATABASE_URI! } }),
  editor: lexicalEditor({}),
  collections: [
    {
      slug: 'pages',
      fields: [
        { name: 'title', type: 'text', required: true },
        { name: 'slug', type: 'text', unique: true },
        { name: 'hero', type: 'group', fields: [
          { name: 'heading', type: 'text' },
          { name: 'image', type: 'upload', relationTo: 'media' },
        ]},
        { name: 'content', type: 'richText' },
      ],
    },
  ],
});

Code Examples

Common usage patterns

Fetch posts in Next.js App Router

Query Payload CMS directly in a Server Component

// app/blog/page.tsx
import { getPayload } from 'payload';
import config from '@payload-config';

export default async function BlogPage() {
  const payload = await getPayload({ config });

  const { docs: posts } = await payload.find({
    collection: 'posts',
    where: { _status: { equals: 'published' } },
    sort: '-publishedAt',
    limit: 10,
    depth: 1, // populate author relationship
  });

  return (
    <div>
      {posts.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>By {typeof post.author === 'object' ? post.author.name : post.author}</p>
        </article>
      ))}
    </div>
  );
}

Custom collection hook

Run server-side logic before saving a document

// In your payload.config.ts collection definition
import { CollectionConfig } from 'payload';

export const Posts: CollectionConfig = {
  slug: 'posts',
  hooks: {
    beforeChange: [
      async ({ data, operation }) => {
        if (operation === 'create') {
          // Auto-generate slug from title
          data.slug = data.title
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, '-')
            .replace(/^-|-$/g, '');
        }
        return data;
      },
    ],
    afterChange: [
      async ({ doc }) => {
        // Revalidate Next.js cache on publish
        if (doc._status === 'published') {
          await fetch(`/api/revalidate?path=/blog/${doc.slug}`, { method: 'POST' });
        }
      },
    ],
  },
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'slug', type: 'text', unique: true, admin: { readOnly: true } },
    { name: 'content', type: 'richText' },
  ],
};

Community Notes

Real experiences from developers who've used this tool