Payload CMS 3.0 represents a fundamental shift in how content management systems integrate with modern web frameworks. It's not a separate backend service but a fully integrated part of your Next.js application.
┌─────────────────────────────────────────────────────────────┐
│ Next.js Application │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ Frontend │ │ Payload Admin│ │ Payload API │ │
│ │ Pages/App │ │ Panel │ │ REST/GraphQL │ │
│ │ Directory │ │ (React) │ │ Endpoints │ │
│ └────────┬────────┘ └──────┬───────┘ └───────┬───────┘ │
│ │ │ │ │
│ ┌────────┴───────────────────┴───────────────────┴───────┐│
│ │ Payload Core (Config-based) ││
│ │ - Collections - Globals - Hooks ││
│ │ - Fields - Access Control - Validation ││
│ └─────────────────────────┬──────────────────────────────┘│
│ │ │
│ ┌─────────────────────────┴──────────────────────────────┐│
│ │ Database Adapter ││
│ │ (PostgreSQL / MongoDB / SQLite) ││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
Unlike traditional headless CMS solutions where you have:
- Separate CMS backend (different server/service)
- Separate frontend application
- API calls between them
Payload 3.0 works as:
- One codebase: Your Next.js app contains everything
- One deployment: Deploy once, get frontend + CMS + API
- One server: Everything runs on the same Node.js process
your-nextjs-app/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── (frontend)/ # Your website pages
│ │ │ ├── page.tsx
│ │ │ └── about/page.tsx
│ │ ├── (payload)/ # Generated by Payload
│ │ │ ├── admin/ # Admin panel routes
│ │ │ └── api/ # API routes
│ │ └── layout.tsx
│ ├── payload/ # Payload configuration
│ │ ├── collections/ # Data models
│ │ ├── globals/ # Single-instance data
│ │ └── payload.config.ts # Main config
│ └── components/ # Shared React components
├── public/ # Static assets
└── package.json # Single dependency list
When you run pnpm build:
-
Payload generates:
- Admin panel React components
- API route handlers
- TypeScript types from your schema
- Database migrations
-
Next.js compiles:
- Your frontend pages
- Payload's generated admin UI
- API routes (both yours and Payload's)
- Optimizes everything for production
-
Output:
- Single
.nextbuild directory - Standalone deployment package
- All assets optimized and bundled
- Single
When deployed, your application handles:
/→ Your Next.js frontend pages/admin→ Payload admin panel/api→ Combined API routes:- Your custom API routes
- Payload's REST endpoints (
/api/users,/api/posts, etc.) - GraphQL endpoint (
/api/graphql)
User Request → Next.js Router →
├─→ Frontend Route → React Server Component →
│ └─→ Payload Local API → Database
├─→ Admin Route → Payload Admin UI →
│ └─→ Payload API → Database
└─→ API Route → Payload REST/GraphQL → Database
import { buildConfig } from 'payload/config'
import { postgresAdapter } from '@payloadcms/db-postgres'
export default buildConfig({
serverURL: process.env.NEXT_PUBLIC_SERVER_URL,
collections: [
// Your data models
],
globals: [
// Your singletons
],
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI,
},
}),
// Integrates with Next.js
admin: {
bundler: webpackBundler(),
meta: {
titleSuffix: '- Payload',
},
},
})import { withPayload } from '@payloadcms/next/withPayload'
export default withPayload({
// Your Next.js config
})// In a React Server Component
import { getPayloadHMR } from '@payloadcms/next/utilities'
export default async function Page() {
const payload = await getPayloadHMR({ config })
const posts = await payload.find({
collection: 'posts',
limit: 10,
})
return <div>{/* Render posts */}</div>
}- One repository to manage
- Shared TypeScript types
- Unified build process
- Single test suite
- No network latency between frontend and CMS
- Shared Node.js process
- Optimized data fetching with React Server Components
- Single deployment reduces infrastructure overhead
- Hot module replacement works across entire stack
- Unified debugging
- Consistent environment variables
- Simplified local development
- Deploy to any Node.js host
- No need for separate services
- Reduced infrastructure costs
- Easier scaling (scale everything together)
export async function generateStaticParams() {
const payload = await getPayloadHMR({ config })
const posts = await payload.find({
collection: 'posts',
limit: 1000,
})
return posts.docs.map((post) => ({
slug: post.slug,
}))
}// Payload handles draft state automatically
const { docs } = await payload.find({
collection: 'pages',
draft: true, // Include drafts for preview
})// Same auth works for both admin and frontend
import { getPayloadHMR } from '@payloadcms/next/utilities'
const payload = await getPayloadHMR({ config })
const user = await payload.auth({ headers })Both Next.js and Payload share the same environment:
NEXT_PUBLIC_*for client-side- Regular vars for server-side
- No duplication needed
- Use
standaloneoutput for Docker - Payload admin UI is code-split automatically
- API routes are optimized by Next.js
- Horizontal scaling works normally
- Database connections pooled automatically
- File uploads need shared storage (S3)
If migrating from Payload 2.x (separate backend):
- Move Payload config into Next.js app
- Update imports to use
@payloadcms/next - Migrate API calls to use Local API
- Remove separate Payload server
- Update deployment configuration
Payload 3.0's architecture represents a paradigm shift in CMS design:
- Not a separate service: Fully integrated with Next.js
- Config-driven: Define schema once, get everything
- Full-stack by default: Frontend, backend, and admin in one
- Modern patterns: Leverages React Server Components
- Developer-friendly: Reduces complexity while increasing capability
This architecture makes Payload ideal for Next.js projects that need content management without the complexity of managing separate services.