What is Payload CMS?
Payload is a headless CMS and application framework built entirely in TypeScript. Unlike Strapi or Contentful, Payload generates a full admin panel, REST API, and GraphQL API from your TypeScript config — with zero code generation and full type safety.
Why Payload?
- Free and open-source — MIT license, self-hosted
- TypeScript-native — config IS your schema, types are automatic
- Next.js integration — runs inside your Next.js app (v3)
- Database flexible — PostgreSQL, MongoDB, SQLite
- Admin panel — beautiful, auto-generated from config
- Access control — field-level permissions built in
Quick Start
npx create-payload-app@latest my-cms
cd my-cms && npm run dev
# Admin panel at http://localhost:3000/admin
# API at http://localhost:3000/api
Define Collections
// collections/Posts.ts
import type { CollectionConfig } from 'payload';
export const Posts: CollectionConfig = {
slug: 'posts',
admin: {
useAsTitle: 'title'
},
access: {
read: () => true,
create: ({ req: { user } }) => Boolean(user),
update: ({ req: { user } }) => user?.role === 'admin',
},
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'slug', type: 'text', unique: true, admin: { position: 'sidebar' } },
{ name: 'content', type: 'richText' },
{ name: 'featuredImage', type: 'upload', relationTo: 'media' },
{ name: 'author', type: 'relationship', relationTo: 'users' },
{ name: 'tags', type: 'array', fields: [
{ name: 'tag', type: 'text' }
]},
{ name: 'status', type: 'select', options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' }
], defaultValue: 'draft' },
{ name: 'publishedAt', type: 'date' }
]
};
Auto-Generated API
# REST API (automatic)
GET /api/posts # List posts
GET /api/posts/:id # Get single post
POST /api/posts # Create post
PATCH /api/posts/:id # Update post
DELETE /api/posts/:id # Delete post
# With query parameters
GET /api/posts?where[status][equals]=published&sort=-publishedAt&limit=10
# GraphQL (also automatic)
POST /api/graphql
Use in Next.js Pages
// app/blog/[slug]/page.tsx
import { getPayload } from 'payload';
import config from '@payload-config';
export default async function BlogPost({ params }: { params: { slug: string } }) {
const payload = await getPayload({ config });
const { docs } = await payload.find({
collection: 'posts',
where: { slug: { equals: params.slug } }
});
const post = docs[0];
if (!post) return notFound();
return (
<article>
<h1>{post.title}</h1>
<RichText content={post.content} />
</article>
);
}
Payload vs Alternatives
| Feature | Payload | Strapi | Contentful | Sanity |
|---|---|---|---|---|
| Cost | Free (self-host) | Free (self-host) | $300/mo | $99/mo |
| TypeScript | Native | Plugin | SDK | GROQ |
| Next.js integration | Built-in (v3) | API calls | API calls | Plugin |
| Admin UI | Auto-generated | Auto-generated | Hosted | Hosted |
| Database | PG, Mongo, SQLite | PG, MySQL, SQLite | Cloud only | Cloud only |
| Access control | Field-level | Role-based | Role-based | GROQ filters |
Real-World Impact
A media company used Contentful at $300/month for 5 editors. As content grew, they hit API rate limits and needed the $750 plan. After migrating to Payload on a $20 VPS: unlimited API calls, unlimited content, custom admin workflows, and the same editors found the auto-generated admin panel more intuitive. Saved $8,760/year.
Building content-driven applications? I help teams choose and implement the right CMS. Contact spinov001@gmail.com or explore my data tools on Apify.
Top comments (0)