Product manager, designer, or business owner? This guide is written for developers. For what Payload 4 means for your budget, team and risk, read Payload 4 for business owners.
Payload CMS 4.0 is no longer just an early roadmap preview or an experimental branch.
With active canary releases publishing across the entire ecosystem—including payload, @payloadcms/ui, database adapters, and @payloadcms/tanstack-start—and a comprehensive official 3.0 to 4.0 migration guide now merged into Payload's main branch, the shape of Payload 4 is concrete.
Payload 4 is not a mere version bump. It introduces fundamental behavioral shifts in access control security, database versioning defaults, TypeScript typing, and framework decoupling.
Note
This guide is continuously maintained against the official Payload v4 migration guide, canary releases, and active RFCs. It reflects the architecture being finalized for the stable release.
At a glance (v4 canary & migration tracker)
Current release train:v4.0.0-canary.37 (active cross-package canary ecosystem)
Latest stable 3.x release: Payload v3.90.2 (maintenance and hardening branch)
Production recommendation: Run Payload 3.x for current client deployments, but audit schemas, Local API queries, and auth types against the breaking changes outlined below.
Below is the definitive technical breakdown of what is changing in Payload 4.0, categorized into nine core architectural pillars, followed by the four highest-priority checks for production teams.
1. Breaking API changes and security defaults
The most consequential changes in Payload 4 involve subtle shifts in default behavior that directly affect application security and query performance.
overrideAccess: false by default in Local API
In Payload 3, invoking Local API methods (payload.find, payload.findByID, payload.create, etc.) without specifying overrideAccess effectively defaulted to bypassing access control. Local code executed with implicit superadmin privileges unless you explicitly passed overrideAccess: false alongside a user object.
In Payload 4, overrideAccess defaults to false:
typescript
// Payload 3 behavior:// Executed as superadmin, ignoring collection access rulesconst results = await payload.find({
collection: 'orders',
});
// Payload 4 behavior:// Enforces access control! If no user is present in req,// this query runs as unauthenticated / public access.const results = await payload.find({
collection: 'orders',
overrideAccess: true, // Required if you intend to bypass access control
});
The same Local API call follows different defaults depending on the Payload version:
Diagram
Without a user, access rules evaluate a public request. Use an explicit bypass only for trusted server-side work such as seeds and migrations.
This is a massive security hardening step. It prevents accidental data exposure in custom Next.js server actions, Route Handlers, and API endpoints where developers forgot to enforce user access. However, it will break existing cron jobs, migrations, seed scripts, and internal APIs that omitted overrideAccess: true.
Default relationship depth drops from 2 to 1
In Payload 3, relationship fields populated related documents up to a depth of 2 by default. While convenient for rapid prototyping, this frequently caused catastrophic query cascades on nested schemas, dragging database response times down on high-traffic sites.
In Payload 4, the default query depth is reduced from 2 to 1:
typescript
// Payload 3: populated post -> author -> author's avatar// Payload 4: populates post -> author (author's avatar remains an ID string)const post = await payload.findByID({
collection: 'posts',
id: '123',
});
// To restore deeper nesting on a specific query:const postWithDeepRelations = await payload.findByID({
collection: 'posts',
id: '123',
depth: 2,
});
One fewer populated level changes the response shape as well as the amount of database work:
Diagram
Solid arrows show populated relationships; the dashed arrow shows an ID reference. IDs may be strings or numbers depending on the adapter and ID configuration. Set depth: 2 on queries that need the avatar document.
You can restore the global default across your entire application in payload.config.ts:
The utility functions used to construct mock or synthetic Payload requests in custom endpoints and background workers have been standardized:
createLocalReq is removed and replaced by createPayloadRequest.
The old web-request-oriented createPayloadRequest is renamed to createPayloadRequestFromWebRequest.
Truthful configuration types (removal of DeepRequired)
In Payload 3, internal sanitized types like SanitizedConfig and SanitizedCollectionConfig wrapped properties in DeepRequired. This created a false sense of security in TypeScript, masking fields that were actually undefined at runtime.
Payload 4 removes DeepRequired from sanitized config types. If your plugins or custom admin components inspect config or collection settings, TypeScript may now flag properties that require explicit optional chaining (config.admin?.user).
Synchronous sanitization
Config sanitization and rich-text schema sanitization routines that previously returned Promises in multiple lifecycle stages have been refactored into synchronous functions, reducing asynchronous overhead during application bootstrap.
2. Database architecture and versioning by default
Payload 4 makes a fundamental shift in how document history and database schemas are handled.
versions: true by default across collections and globals
In Payload 3, collections and globals did not maintain document versions unless you explicitly configured versions: { drafts: true } or versions: true.
In Payload 4, versioning is enabled by default for all collections and globals, while drafts remain disabled by default.
Warning
If you upgrade an existing project without modifying your config, Payload 4 will generate migration scripts to create _versions database tables for every single collection and global in your schema!
If you operate a high-volume database or prefer lean, unversioned tables, you must explicitly opt out:
For a relational adapter, the versioning decision determines which tables a migration must create:
Diagram
This shows the main and version-history tables, not every auxiliary table. MongoDB uses collections instead. Review generated migrations before applying them; the built-in Users collection explicitly disables versions.
The official codemod (npx @payloadcms/codemod) includes an automated transform to inject versions: false across all collections that were unversioned in Payload 3, ensuring existing databases do not experience unexpected table bloat. For teams managing production PostgreSQL instances, read our guide on why you should stop running Payload CMS in push mode and switch to migration-only workflows.
readVersions permission inheritance
Previously, if a collection enabled versions without defining a specific readVersions access control function, version queries defaulted to requiring full authentication.
In Payload 4, if readVersions is undefined, it inherits the collection's standard read access rule. If a collection has public read access, its published historical versions share that read access unless explicitly restricted with a dedicated readVersions rule.
Database adapter type exports
Import paths for database adapters have been flattened. Subpath imports like @payloadcms/db-postgres/types and @payloadcms/db-mongodb/types have been eliminated. All types and utilities now export directly from the package root:
Authentication in Payload 4 undergoes both architectural hardening and a major TypeScript overhaul.
Unified user types and typed req.user
In Payload 3, user typing was fragmented across TypedUser, UntypedUser, and ClientUser. Access control functions and custom hooks frequently forced developers to cast req.user as User to avoid lint and compiler errors.
Payload 4 consolidates these types into two primary constructs:
User: The generic representation of a user document in your system.
AuthenticatedUser: The strongly-typed user attached to an authenticated request.
Via PR #17151, req.user is now properly typed as AuthenticatedUser throughout all hooks, endpoints, and access control callbacks. Crucially, req.user now natively includes runtime authentication metadata:
typescript
// Access control function in Payload 4:exportconstcanEditTenant: Access = ({ req }) => {
if (!req.user) returnfalse;
// Strongly typed runtime auth properties:const strategy = req.user._strategy; // e.g., 'jwt', 'api-key'const sessionId = req.user._sid; // Active session identifierreturn req.user.role === 'admin';
};
JWT tokens issued by Payload 4 now include an authVersion field embedded directly within the protected JWT header. The payload shape remains flat and performant, but the header versioning ensures that:
Stale tokens generated under incompatible Payload 3 auth strategies can be safely rejected rather than throwing cryptic runtime deserialization errors.
In-flight token rotations and strategy migrations can be performed without session corruption.
4. Admin UI, semantic tokens, and Tailwind compatibility
Payload's admin panel receives its most substantial design refresh since the 3.0 launch, focusing on visual density, custom branding, and modern frontend styling interoperability.
Diagram
The sidebar organizes content; drawers keep related editing in context. CSS tokens control the presentation, while reset isolation supports custom Tailwind views.
Component configuration alignment for Globals
In Payload 3, configuring custom admin components for globals used a different property structure than collections (admin.components.elements vs admin.components.edit).
Payload 4 unifies globals with collections:
globalConfig.admin.components.elements is renamed to globalConfig.admin.components.edit.
Globals gain full parity with collections for defining custom views, tabs, and action bars.
Elimination of Sass in favor of semantic CSS tokens
The legacy Sass (scss) build pipeline has been completely removed from @payloadcms/ui. All admin styling now relies on pure CSS and semantic design tokens configured via CSS variables.
This provides two major wins:
Effortless admin theming: Agencies and platform builders can rebrand the Payload admin interface by overriding standard tokens (brand colors, neutral scales, surface radiuses, borders) without compiling Sass assets or overriding deeply nested CSS classes.
Faster build times: Removing Sass eliminates heavy preprocessing steps during local development and containerized deployments.
Tailwind CSS reset isolation
In Payload 3, using Tailwind CSS or component libraries like shadcn/ui inside custom admin views was prone to style pollution: the admin panel's base styles bled into Tailwind elements, while Tailwind's Preflight reset broke default Payload field styling.
Payload 4 adopts an isolated CSS reset strategy. Custom admin views, custom fields, and dashboard widgets can now use modern Tailwind classes without conflict, making it seamless to construct bespoke client experiences.
Native hierarchies, folders, and tags
Payload 4 brings hierarchical content organization into core:
Folders: Single-parent containment for grouping pages, documents, and media assets.
Tags: Multi-dimensional categorization across collections.
Sidebar tabs: The admin sidebar supports expandable tabs and custom navigational panels, allowing editors to browse documents via content trees rather than flat, paginated tables.
This reduces the need for community plugins like nested-docs and aligns with how enterprise editorial teams organize large publishing corpuses. For an assessment of editor workflows, see our Payload CMS for publishing teams review.
5. Lexical modernization and the removal of Slate
Rich text editing in Payload 4 takes a definitive step forward.
@payloadcms/richtext-slate is completely removed
The legacy Slate rich-text adapter is officially deleted in Payload 4. @payloadcms/richtext-lexical is now the sole official, built-in rich-text engine.
If your project still relies on Slate, you must migrate your data to Lexical before upgrading to v4:
bash
# Slate is not installable or supported on Payload 4
pnpm remove @payloadcms/richtext-slate
pnpm add @payloadcms/richtext-lexical@canary
Payload provides migration scripts and AST converters to transition existing Slate JSON documents to Lexical's node-based tree structure.
Lexical 0.50 upgrade and table stabilization
Payload 4 upgrades the underlying Lexical framework from version 0.41 to Lexical 0.50. Key enhancements include:
TableFeature graduation: EXPERIMENTAL_TableFeature is stabilized and renamed to TableFeature.
Stricter link nodes: Link node structures are validated against stricter schemas, preventing malformed URLs and broken target attributes.
Removal of deprecated converters: Legacy HTML/Markdown converter functions from early 3.x releases have been pruned in favor of the unified @payloadcms/richtext-lexical converter utilities.
6. List view performance and the Select API
Query efficiency in the admin panel has been overhauled to handle collections with millions of rows.
List View Select API enabled by default
In Payload 3, loading an admin list view fetched entire document objects from the database unless you explicitly configured admin.enableListViewSelectAPI: true. On collections with large rich-text fields or deep relations, opening the admin table caused significant server memory consumption and slow query times.
In Payload 4:
The Select API is active by default.
The config option admin.enableListViewSelectAPI has been removed.
Payload automatically crafts database queries that project only the columns currently visible in the editor's active table view.
forceSelect replaced by configurable select()
The static forceSelect: boolean property on field definitions has been deprecated. In its place, Payload 4 introduces a dynamic select() function, allowing field developers to programmatically determine whether a field should be included in database selections based on user permissions or context.
7. Developer tooling, schemas, and the official codemod
Payload 4 introduces first-class CLI commands and automated migration tooling to streamline upgrades.
The official migration codemod (npx @payloadcms/codemod)
To assist developers in transitioning from 3.x to 4.0, Payload provides an automated codemod:
bash
npx @payloadcms/codemod@latest
The codemod inspects your repository and automatically performs mechanical transformations:
Injects versions: false into collections that lacked explicit versioning, preserving existing database schema shapes.
In Payload 3, adding custom CLI commands relied on configuring config.bin, which lacked formal typing and argument parsing.
Payload 4 replaces config.bin with config.cli.commands, utilizing the defineCLICommand helper:
typescript
import { defineCLICommand } from'payload';
exportconst syncSearchIndex = defineCLICommand({
name: 'sync-search',
description: 'Rebuild the vector search index across all articles',
args: {
dryRun: {
type: 'boolean',
short: 'd',
description: 'Run without committing mutations',
},
},
asynchandler({ payload, args }) {
payload.logger.info(`Starting sync with dryRun=${args.dryRun}`);
// Command implementation with full access to the local API
},
});
Custom commands are executed directly via payload cli <command-name>, complete with auto-generated help menus and type-safe argument handling.
typescriptSchema becomes jsonSchema
On field configurations, typescriptSchema has been renamed to jsonSchema. Rather than merely generating static TypeScript types at build time, jsonSchema is preserved at runtime, enabling dynamic schema inspection by validation engines, REST clients, and AI agents.
8. MCP (Model Context Protocol) and AI agent workflows
AI-assisted engineering is a primary focal point of Payload 4. The @payloadcms/plugin-mcp package is published as a first-class v4 canary, tightly coupled with the core architecture.
The MCP boundary separates tool discovery from permission to execute a tool:
Diagram
Exposing a tool does not grant unrestricted data access. The v4 resolver identifies the caller through req.user; built-in tools apply their configured MCP access checks before execution.
Key MCP advancements in Payload 4 include:
Opt-out tools by default: In 4.0, built-in collection and global tools are exposed automatically unless disabled. Review that exposure when upgrading: the default resolver authenticates a Payload user API key and built-in tools include permission checks; auth tools are separately opt-in. Tool exposure is not permission to bypass access control. See the official MCP migration notes.
Runtime jsonSchema ingestion: Because fields expose standard JSON Schemas at runtime, MCP servers pass exact field types, enum values, and validation constraints directly to LLM tool-calling contexts, dramatically reducing AI hallucination.
Payload Skills: The team maintains standardized agent skill definitions and evaluation suites, training AI assistants on idiomatic Payload 4 hooks, access control, and schema patterns.
9. Framework ecosystem and TanStack Start
Payload 3 tied itself intimately to Next.js and the React Server Components (RSC) App Router. While Next.js remains the primary tier-one platform, Payload 4 establishes an abstracted framework boundary.
@payloadcms/tanstack-start canary
Payload 4 introduces the @payloadcms/tanstack-start canary adapter. It is an experimental integration for evaluating Payload backend and admin rendering with:
TanStack Start
TanStack Router
Vite
The official Payload 4 preview describes a framework-adapter pattern and early TanStack support, rather than stable feature parity. Payload is moving framework-specific wiring out of its core. Teams seeking ultra-fast local Vite HMR, alternative server runtimes, or strict decoupling from Vercel's ecosystem can now leverage Payload with TanStack Start. For an evaluation of these two platforms, read our detailed TanStack Start vs Next.js comparison.
The adapter boundary lets framework-specific rendering and routing connect to the same Payload configuration:
Diagram
These are alternative framework integrations, not two runtimes required by one deployment. Next.js remains the established path; evaluate the TanStack canary independently before a production migration.
Minimum runtime baselines
Running Payload 4 requires modern infrastructure. The minimum supported runtime versions across the v4 branch are:
Prerequisite
Minimum Version
Notes
Node.js
>= 24.15.0
Required for modern native web crypto, streams, and ESM module resolution
Next.js (if using Next adapter)
>= 16.2.6
Aligns with Next.js Turbopack stability and current server component specifications
TypeScript
>= 6.0.3
Necessary for advanced type inference and schema type synthesis
The Big 4: Critical checklist for existing codebases
If you are planning an upgrade from Payload 3 to Payload 4, focus your engineering review on these four critical areas:
Treat these reviews as gates before generating a migration for an existing database:
Diagram
The codemod handles mechanical edits. Access decisions and database migration review still require a developer; inspect generated changes before applying them to a database.
Audit Local API calls (overrideAccess: false):
Search your codebase for all instances of payload.find, payload.create, payload.update, and payload.delete. If a query runs inside an internal task, migration script, or administrative server action without a user session, add overrideAccess: true. Otherwise, the query will run as public and fail or return empty datasets.
Decide your versioning strategy (versions: true default):
Do not run database migrations against Payload 4 without reviewing collection version configurations. If you do not want version tables created for all collections, either run npx @payloadcms/codemod to explicitly set versions: false or prepare your database capacity for new versioning tables.
Refactor user typing in hooks and access rules:
Replace legacy TypedUser imports with AuthenticatedUser. Check custom access control functions that inspected req.user properties to take advantage of typed _strategy and _sid.
Prepare infrastructure for Node 24 and Next.js 16.2.6+:
Ensure your Dockerfiles, CI/CD runners (GitHub Actions), and hosting environments (self-hosted Docker, Coolify, or VPS) are upgraded to Node.js 24 and support TypeScript 6.
Recommended upgrade path
When you are ready to evaluate the upgrade in a test branch:
Branch cleanly: Create a dedicated feature/payload-4-migration branch.
Upgrade dependencies: Point package.json to 4.0.0-canary.37 (or the latest canary release) across all @payloadcms/* packages.
Run the codemod: Execute npx @payloadcms/codemod to apply baseline code transformations.
Run TypeScript verification: Run npx tsc --noEmit to identify any residual type discrepancies resulting from the removal of DeepRequired or user type updates.
Generate and inspect migrations: Run payload migrate:create against a local test database and inspect the generated SQL/migration files before applying them.
If you want an experienced team to audit your schemas, plugins, and custom admin extensions ahead of the stable v4 rollout, schedule a Payload CMS audit or engage an expert Payload CMS developer. For larger replatforming initiatives, explore our dedicated Payload CMS migration services.