Payload is a TypeScript-first headless CMS that generates REST and GraphQL APIs from your config. It runs on Next.js, gives you full code control, and is completely free and open source.
Why Payload?
- Code-first — config in TypeScript, not GUI clicks
- Next.js native — runs inside your Next.js app
- Auto REST + GraphQL — generated from your schema
- Auth built-in — users, roles, field-level access control
- Open source — MIT license, self-host free
Quick Start
npx create-payload-app@latest my-cms
cd my-cms
npm run dev
# Admin: http://localhost:3000/admin
# API: http://localhost:3000/api
Define Collections (Schema)
// collections/Articles.ts
import type { CollectionConfig } from 'payload';
export const Articles: CollectionConfig = {
slug: 'articles',
admin: {
useAsTitle: 'title',
},
access: {
read: () => true,
create: ({ req: { user } }) => !!user,
},
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'slug', type: 'text', unique: true },
{ name: 'content', type: 'richText' },
{ name: 'status', type: 'select',
options: ['draft', 'published', 'archived'],
defaultValue: 'draft',
},
{ name: 'author', type: 'relationship', relationTo: 'users' },
{ name: 'tags', type: 'array',
fields: [{ name: 'tag', type: 'text' }],
},
{ name: 'coverImage', type: 'upload', relationTo: 'media' },
{ name: 'publishedAt', type: 'date' },
],
};
REST API (Auto-Generated)
# List articles
curl http://localhost:3000/api/articles
# Filter + sort + paginate
curl 'http://localhost:3000/api/articles?where[status][equals]=published&sort=-publishedAt&limit=10'
# Get by ID
curl http://localhost:3000/api/articles/ARTICLE_ID
# Create (authenticated)
curl -X POST http://localhost:3000/api/articles \
-H 'Authorization: Bearer TOKEN' \
-H 'Content-Type: application/json' \
-d '{"title": "Hello World", "status": "published"}'
# Update
curl -X PATCH http://localhost:3000/api/articles/ARTICLE_ID \
-H 'Authorization: Bearer TOKEN' \
-H 'Content-Type: application/json' \
-d '{"status": "archived"}'
# Upload media
curl -X POST http://localhost:3000/api/media \
-H 'Authorization: Bearer TOKEN' \
-F 'file=@photo.jpg'
Local API (Server-Side — Type-Safe)
// In your Next.js pages/API routes
import { getPayload } from 'payload';
import config from '@payload-config';
const payload = await getPayload({ config });
// Fully typed queries
const articles = await payload.find({
collection: 'articles',
where: { status: { equals: 'published' } },
sort: '-publishedAt',
limit: 10,
});
// Create
const newArticle = await payload.create({
collection: 'articles',
data: {
title: 'New Article',
status: 'draft',
},
});
Key Features
| Feature | Details |
|---|---|
| Framework | Next.js native |
| API | REST + GraphQL + Local (type-safe) |
| Database | Postgres, MongoDB |
| Auth | Built-in, field-level access |
| Media | Upload, image resize, S3 |
| Versions | Draft/publish, revisions |
| Localization | Built-in i18n |
| Live Preview | Real-time content preview |
Resources
Building content-driven apps? Check my Apify actors or email spinov001@gmail.com.
Top comments (0)