Releases: payloadcms/payload
v3.80.0
v3.80.0 (2026-03-20)
🚀 Features
- add
disableUniqueproperty to the slug field for better multi tenant plugin support (#15963) (395e1ed)
🐛 Bug Fixes
- unpublish updates existing version instead of creating a new one (#15985) (67063ab)
- use latest draft version data when trashing unpublished documents (#15981) (57a0edc)
- db-mongodb: fix pagination with collation in transactions (#15990) (5c935aa)
- db-postgres:
nearquery can give incorrect results (#15907) (843306c) - db-sqlite: scheduled publish does not show upcoming events because of wrong nested json querying (#15911) (3fa834a)
- deps: resolve high severity audit vulnerabilities (#15961) (da212fd)
- drizzle: pagination applied incorrectly when sorting by a field inside an array field (#15908) (0f67215)
- drizzle: groups inside localized tabs are not working correctly (#15936) (57bde77)
- graphql: force nullable for relationships to avoid errors when the related document is related (#15915) (560cabe)
- plugin-mcp: plugin can break next.js request handler because underlying Hono library modifies the global Request object (#15938) (4cfa495)
- plugin-multi-tenant: modal container blocks clicks on create-first-user page (#15973) (8157f4c)
- richtext-lexical: richtext fields don't respect RTL direction for Arabic and other RTL locales (#15964) (944a889)
- ui: defer live preview iframe rendering (#15999) (9999083)
- ui: stale data modal shown when onChange starts while save is in-flight (#15960) (dc98f0f)
📚 Documentation
- add custom admin location docs (#15581) (04a4b0a)
- updating lexical richtext fields (#15968) (8e5d56d)
- add copy and paste documentation for blocks (#15987) (747be91)
- warning for numeric field names (#15966) (99dcac1)
- storage adapters improvements (#15946) (9815d83)
📝 Templates
⚙️ CI
🏡 Chores
- add docker:clean script and fix docker:start reliability (#16000) (2646018)
- bump monorepo Next.js version to 16.2.0 (#15992) (a188556)
- consolidate docker scripts into single docker-compose with profiles (#15974) (f89e736)
- examples: update astro package.json name (#15118) (4e1c04e)
🤝 Contributors
- Alessio Gravili (@AlessioGr)
- Jarrod Flesch (@JarrodMFlesch)
- German Jablonski (@GermanJablo)
- Jake Fletcher (@jacobsfletch)
- Sasha (@r1tsuu)
- Patrik (@PatrikKozak)
- Sean Zubrickas (@zubricks)
- Riley Langbein (@rilrom)
- Elliot DeNolf (@denolfe)
v3.79.1
v3.79.1 (2026-03-16)
🐛 Bug Fixes
- text field validation rejecting localized object values (#15932) (fac59c8)
- use Sec-Fetch-Site header for cookie authentication validation (#15751) (ef507a6)
- improved request origin retrieval (#15919) (f30d34f)
- generate:types inlines all blocks, add
forceInlineBlocksproperty to use in plugin mcp (#15892) (6a9e367) - update broken custom components docs link in config types jsdoc (#15794) (17aa1b5)
- run
sanitizeWhereQueryfor join query access result (#15891) (dc049fe) - early return out of me access (#15883) (c6054c5)
- scope orderable join reordering by parent relation (#15842) (17a0d19)
- stricter input validation (#15868) (e474205)
- drizzle: avoid ts errors for
payload generate:db-schemawith circular references (#15895) (66a2efa) - drizzle: error when using contains operator on hasMany select fields (#15865) (fba2438)
- drizzle: correctly apply query limit on polymorphic joins (#15652) (fe36dde)
- plugin-import-export: add space in zh translation for exportDocu… (#15833) (43d5596)
- plugin-mcp: bump @modelcontextprotocol/sdk from 1.25.2 to 1.27.1 (#15942) (94d2249)
- storage-azure: add stream aborts for error handling and connection closure (#15768) (b2a03c9)
- ui: stale data modal incorrectly shown when user saves their own document (#15933) (a5d9388)
- ui: copy & pasting block content duplicates array items in editor UI (#15941) (9bcedc8)
- ui: deleted array item reappears after reorder with autosave (#15906) (752c15a)
- ui: use consistent empty state styling in relationship table (#15914) (93b90da)
- ui: clicking filtered Combobox entries fails to trigger selection (#15788) (de3e5ae)
- ui: document status shows changed after publishing specific locale (#15765) (b95df0b)
- ui: split only on first colon in toast error messages (#15894) (fd64504)
- ui: block clipboard paste causes duplicate ID errors in Postgres (#15863) (e7d6331)
- ui: monomorphic relationship fields don't support multi-select with in/not_in operators (#15886) (f71ef61)
- ui: equal column widths for block-drawer blocks (#15867) (07f7802)
- ui: isolate join table column preferences from list view (#15846) (649f117)
- ui: falling back to UTC timezones in timezone picker (#15841) (70099b7)
⚡ Performance
📚 Documentation
- correct type name in editMenuItems client component example (#15904) (03b20d0)
- adds req to available args and wraps examples with proper String type conversions in nested-docs (#15931) (d2a0740)
- adds docs for logger config (#15927) (46e43fc)
- fix links to virtual relationship documentation in both Blocks and Array field documentation (#15888) (fff60c8)
- broken anchor link in blocks field table (#15887) (36c051a)
- examples: clarify MongoDB prerequisites in Mongo-backed examples (#15860) (2aa973f)
- plugin-mcp: updates MCP plugin documentation (#15729) (b97b4e7)
🧪 Tests
- adjust total test count to exclude todo tests in summary output (#15943) (e46daec)
- fixes pagination and sorting list-view tests due to hydration timing issues (#15925) (b0f00c4)
- flaky timeout when clicking Create New button in versions test suite (#15850) (3fb10e1)
🏡 Chores
🤝 Contributors
- Jessica Rynkar (@JessRynkar)
- Patrik (@PatrikKozak)
- German Jablonski (@GermanJablo)
- Copilot (@Copilot)
- Leon Gattermayer (@LeonGatt)
- Omar Yusuf Abdi (@omar-y-abdi)
- Sean Zubrickas (@zubricks)
- Eduardo Costa (@ed-cscosta)
- Jarrod Flesch (@JarrodMFlesch)
- Sasha (@r1tsuu)
- Alessio Gravili (@AlessioGr)
- Seiya (@silmin)
- Maxim Seshuk (@maximseshuk)
- Kendell (@kendelljoseph)
- Divyam gupta (@divyamdotfoo)
- deepshekhardas (@deepshekhardas)
- Paul (@paulpopus)
- zzz1220 (@zzz1220)
- Mikko Vänskä (@vanska)
v3.79.0
v3.79.0 (2026-03-04)
🚀 Features
- richtext-lexical: separate configuration for lexical block icons (#15632) (f0498f2)
- richtext-lexical: upgrade lexical from 0.35.0 to 0.41.0 (#15760) (ba3bd74)
- translations: add i18n translations for modular dashboards (#15004) (cef2838)
Separate Block Icon Configuration (richtext-lexical) — Configure different images for Lexical block icons and block drawer thumbnails independently. Previously, imageURL served both contexts, forcing a compromise between a good 20x20px icon and a good drawer thumbnail. The new images property supports separate icon and thumbnail values with automatic fallback. Fully backwards compatible — imageURL still works but is deprecated. #15632
const QuoteBlock: Block = {
slug: 'quote',
images: {
icon: 'https://example.com/icons/quote-20x20.svg',
thumbnail: { url: 'https://example.com/thumbnails/quote-480x320.jpg', alt: 'Quote block' },
},
fields: [...],
}Lexical Upgrade 0.35.0 → 0.41.0 (richtext-lexical) — Upgrades the Lexical rich text editor dependency from v0.35.0 to v0.41.0. Includes upstream fixes like normalizeMarkdown (facebook/lexical#7812). All Lexical breaking changes are handled internally by Payload — no action required for standard usage. If you installed lexical manually, update it to 0.41.0 (though using the re-exported versions from @payloadcms/richtext-lexical/lexical/* is recommended). #15760
Modular Dashboard Translations (translations) — Adds i18n translation support for the Modular Dashboards feature, covering all dashboard widget buttons and error messages. Previously, dashboard UI elements lacked translation keys, making them inaccessible for non-English users. Also updates the automatic translation script to use GPT-4.1 for improved cost efficiency. #15004
🐛 Bug Fixes
- restoreVersion validation for localized required fields (#15821) (e899182)
- draft doc validation when duplicating docs (#15816) (f470699)
- plugin-ecommerce: pass req to Payload API calls in Stripe adapter (#15839) (74799ea)
- plugin-import-export: automatically inherit locale and limit from URL queries (#15812) (ee083f0)
- plugin-import-export: fix imports with locales in a different column order than exported (#15808) (410912c)
- plugin-import-export: fix exports in other non-latin scripts being broken when opened in excel (#15813) (d931894)
- ui: drag and drop not working for sortable hasMany fields (#15845) (2c7ef3f)
- ui: prevent false positive stale data modal on autosave-enabled documents (#15817) (6aff717)
- ui: typo in CodeEditor export statement (#15795) (c5b2a91)
🛠 Refactors
📚 Documentation
- add documentation for conditional tabs (#15809) (a1d6733)
- fix table formatting in import-export plugin (#15792) (0dba95a)
🧪 Tests
🏡 Chores
- prevent dev server from dirtying tracked files (#15826) (cb6f426)
- plugin-search: clean up .DS_Store file and resulting empty images directory (#14506) (f6f73dd)
⚠️ BREAKING CHANGES
-
rename widget ComponentPath to Component for consistency (#15780) (f7d0d04)
- Renames
Widget.ComponentPathtoWidget.Componentand types it as PayloadComponentinstead ofstring` - This aligns dashboard widgets with every other component reference in (collections, globals, fields, admin components) - none of them path in the property name, and all of them are typed as
PayloadComponent - Enables new typescript plugin to work for widget paths (the plugin uses
PayloadComponentcontextual type detection -string-typed properties were invisible to it)
- Renames
🤝 Contributors
- Patrik (@PatrikKozak)
- Jens Becker (@jhb-dev)
- Paul (@paulpopus)
- German Jablonski (@GermanJablo)
- Elliot DeNolf (@denolfe)
- Luiz Cláudio (@lcnogueira)
- Palamar Roman (@VeiaG)
- Sebastian Blank (@blankse)
- Alessio Gravili (@AlessioGr)
- Mahmoud Hassan (@Mhmod-Hsn)
v3.78.0
v3.78.0 (2026-02-27)
🚀 Features
- typescript plugin for import paths (#15779) (7639664)
- move trash out of beta and delete access can now be limited to trash only (#15210) (2347cd9)
- next, ui: widget fields (#15700) (0a123b5)
- plugin-mcp: out of beta (#15711) (1b19d5f)
- plugin-mcp: ignore virtual fields in create and update operations (#15680) (cc8f888)
- richtext-lexical: add markdown transformer for upload nodes (#15630) (4af5a85)
- ui: adds dashed button style (#15728) (d570787)
- ui: make query-presets editable from the form view (#15657) (575f8e9)
Feature Details
TypeScript Plugin for Component Paths - New @payloadcms/typescript-plugin validates PayloadComponent import paths directly in your IDE. It checks that referenced files and exports exist, provides autocomplete for file paths and export names, supports go-to-definition on component path strings, and understands all Payload path conventions including absolute paths, relative paths, tsconfig aliases, and package imports. #15779
screenshot.2026-02-26.at.15.55.40.mp4
pnpm add -D @payloadcms/typescript-plugin{
"compilerOptions": {
"plugins": [{ "name": "next" }, { "name": "@payloadcms/typescript-plugin" }]
}
}Trash Out of Beta with Granular Delete Access - Trash is now a stable feature. Delete access control can now distinguish between trashing and permanently deleting — allowing you to permit users to soft-delete documents while restricting permanent deletion to admins. When data.deletedAt is being set, the operation is a trash; otherwise it's a permanent delete. #15210
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',
trash: true,
access: {
delete: ({ req: { user }, data }) => {
// Not logged in - no access
if (!user) {
return false
}
// Admins can do anything (trash or permanently delete)
if (user.roles?.includes('admin')) {
return true
}
// Regular users: check what operation they're attempting
// If data.deletedAt is being set, it's a trash operation - allow it
if (data?.deletedAt) {
return true
}
// Otherwise it's a permanent delete - deny for non-admins
return false
},
},
fields: [
// ...
],
}Widget Fields (next, ui) - Dashboard widgets can now declare configurable fields, similar to Blocks. Widget data is editable from a new drawer UI when in dashboard editing mode. Full type generation is included — WidgetInstance<T> is generic with typed data and width, and WidgetServerProps is generic so widget components receive typed widgetData. #15700
Screen.Recording.2026-02-23.at.16.25.40.mov
import { buildConfig } from 'payload'
export default buildConfig({
admin: {
dashboard: {
widgets: [
{
slug: 'sales-summary',
ComponentPath: './components/SalesSummary.tsx#default',
fields: [
{ name: 'title', type: 'text' },
{
name: 'timeframe',
type: 'select',
options: ['daily', 'weekly', 'monthly', 'yearly'],
},
{ name: 'showTrend', type: 'checkbox' },
],
minWidth: 'small',
maxWidth: 'medium',
},
],
},
},
})import type { WidgetServerProps } from 'payload'
import type { SalesSummaryWidget } from '../payload-types'
export default async function SalesSummaryWidgetComponent({
widgetData,
}: WidgetServerProps<SalesSummaryWidget>) {
const title = widgetData?.title ?? 'Sales Summary'
const timeframe = widgetData?.timeframe ?? 'monthly'
return (
<div className="card">
<h3>
{title} ({timeframe})
</h3>
</div>
)
}MCP Plugin Out of Beta (plugin-mcp) - @payloadcms/plugin-mcp is now stable and ready for production use. #15711
Virtual Field Filtering in MCP (plugin-mcp) - Virtual fields (virtual: true) are now automatically stripped from MCP tool input schemas and filtered from parsed data before create, update, and updateGlobal operations. This prevents non-stored fields from appearing as accepted MCP parameters. #15680
Markdown Transformer for Upload Nodes (richtext-lexical) - Upload nodes are now properly converted when using convertLexicalToMarkdown. Previously, upload nodes were silently dropped during markdown conversion. Now populated image uploads output , non-image uploads output link syntax, and non-populated uploads output a reference placeholder so data is never lost. #15630
Dashed Button Style (ui) - Adds a new dashed button style variant. Also replaces box-shadow with border on all buttons and fixes icon-only button padding. #15728
Editable Query Presets from Form View (ui) - Query presets can now be created and edited directly from the document form view using a full WhereBuilder, column picker, and groupBy selector — no longer requiring the list view to build queries first. #15657
Screen.Recording.2026-02-17.at.18.15.34.mov
🐛 Bug Fixes
- getFieldsToSign crashes when user missing group/tab fields (#15775) (9f0c101)
- improve mobile touch support for dnd (#15771) (418bb92)
- prevent silent data overwrites on concurrent edits (#15749) (7a3f43f)
- preserve block metadata in mergeLocalizedData and filterDataToSelectedLocales (#15715) (6557292)
- return 400 for malformed JSON request bodies (#15706) (4861fa1)
- globals not updating updatedAt when saving drafts (#15764) (df17cb1)
- return early if pasteURL is not defined (#15748) (23d52a0)
- prevent req.file leak between sequential duplicate() calls on upload collections (#15620) (2baea2e)
- sanitize filenames in storage adapters (#15746) (45bd2f1)
- throw error for unknown query operators (#15739) (08226db)
- preserve locale data in unnamed groups with localizeStatus (#15658) (38b8c68)
- next: conditionally query snapshot field based on localization (#15693) (d5706ee)
- plugin-import-export: update docs on jobs and basic usage as well as visibility (#15695) (a40210c)
- plugin-mcp: use inline block schemas in JSON output (#15675) (a66e844)
- plugin-multi-tenant: hasMany tenant fields double-wrap arrays in filterOptions (#15709) (aaddeac)
- plugin-multi-tenant: return false instead of query when no tenants (#15679) ([f5a5bd8](https://github....
v3.77.0
v3.77.0 (2026-02-18)
🚀 Features
- pass local API
depththrough toreq.query.depthfor consistency (#15023) (9a38469) - db-*: add
customIDarg todb.create(#15653) (0935824) - plugin-mcp: migrate from @vercel/mcp-adapter to mcp-handler (#15661) (24025fd)
Feature Details
Local API Depth Consistency - The depth option passed to Local API calls like payload.find() is now automatically set on req.query.depth. Previously, hooks relying on req.query.depth would behave differently between Local API and REST/GraphQL calls unless you manually passed req: { query: { depth: x } } in addition to depth: x. This change ensures consistent behavior across all API methods. #15023
Custom ID Support in db.create (db-*) - New customID argument on payload.db.create allows creating documents with a specific ID without requiring a custom ID field in your collection schema. #15653
payload.db.create({ collection: 'posts', customID: 'ce98d6c4-c3ab-45de-9dfc-bf33d94cc941', data: { } })MCP Plugin Migration (plugin-mcp) - Migrates from the deprecated @vercel/mcp-adapter to mcp-handler and bumps @modelcontextprotocol/sdk to 1.25.2 addressing a security vulnerability. Exposes new handler options: disableSse, onEvent, and redisUrl. #15661
import { mcpPlugin } from '@payloadcms/plugin-mcp'
export default buildConfig({
plugins: [
mcpPlugin({
// Optional: Enable SSE transport (disabled by default)
disableSse: false,
// Optional: Redis URL for SSE session management (defaults to REDIS_URL env)
redisUrl: 'redis://localhost:6379',
// Optional: Track MCP events for analytics/debugging
onEvent: (event) => {
console.log('MCP event:', event)
},
}),
],
})🐛 Bug Fixes
- hasMany text fields cannot be filtered with contains operator (#15671) (4513a05)
- use consistent empty state styling between list and folder views (#15555) (8953b37)
- populate previousValue correctly in afterChange hooks for nested lexical fields (#15623) (1cc3bb9)
- add i18n support for dashboard edit mode buttons (#15564) (818e31d)
- next: handle undefined fieldTab in version diff tabs (#15590) (bbacab8)
- plugin-cloud-storage: ensure file data persists across operations (#15570) (6af3673)
- plugin-cloud-storage: generateFileURL only ran when disablePayloadAccessControl was true (#15667) (6c5611c)
- plugin-import-export: remove deprecated import (#15666) (733b1df)
- plugin-import-export: export and import issues when using custom IDs (#15518) (7e2a3ab)
- plugin-import-export: columns being duplicated when using toCSV hook (#15597) (28e07dc)
- plugin-mcp: resolve union type fields failing in update tool (#15660) (9ae89dd)
- plugin-multi-tenant: improve translation for "Tenant" (use "Mandant" instead of "Mieter") (#15537) (4d4033b)
- plugin-multi-tenant: tenant selector not appearing after login (#15617) (dd09f67)
- storage-r2: build error due to types issue in R2 Bucket type (#15670) (7d1e233)
- ui: fix broken polymorphic join edit drawer (#15621) (d450e99)
📚 Documentation
- update cell component docs (#15574) (c403b00)
- outline html lexical content conversion (#15601) (dc1b799)
- updates collection admin options (#14605) (8e92f7f)
- update custom components docs (#15576) (ae82294)
- clarify supported Next.js versions and optional dependencies in installation guide (#15604) (409bc0d)
🧪 Tests
🏡 Chores
- add md and mdx language block linting (#15309) (5415516)
- bump playwright to fix vscode run test indicators (#15626) (f74d288)
- only add publishAllLocales to publish when localizeStatus is enabled (#15610) (d57bc22)
🤝 Contributors
- Paul (@paulpopus)
- Patrik (@PatrikKozak)
- Jessica Rynkar (@JessRynkar)
- German Jablonski (@GermanJablo)
- Kendell (@kendelljoseph)
- Ahmad Yasser (@AhmadYasser1)
- Sasha (@r1tsuu)
- Philipp Schneider (@philipp-tailor)
- Philipp Brumm (@brumm)
- Sebastian Blank (@blankse)
- Sean Zubrickas (@zubricks)
- Alessio Gravili (@AlessioGr)
- Vaishnav Patil (@vaishnav-3)
- Divyesh Jain (@divyesh123-jain)
v3.76.1
v3.76.1 (2026-02-11)
🐛 Bug Fixes
- use optional chaining for adminThumbnail size lookup to prevent crash (#15586) (6937eec)
- non-image files should not recieve 0 bytes with useTempFiles (#15538) (a313627)
- add CSP headers to SVG uploads to prevent XSS (#15506) (8283c25)
- richtext-lexical: link tooltip overflows outside viewport with long URLs (#15584) (af6b1a1)
- ui: prevent Tabs field crash when stored tabIndex exceeds tab count (#15588) (a9e296e)
- ui: copy to locale function swallowing errors (#15562) (8ce62d8)
- ui: ensure unpublish button only shows when drafts are enabled (#15459) (69dc5e6)
⚙️ CI
v3.76.0
v3.76.0 (2026-02-09)
🚀 Features
- plugin-import-export: adds new exportLimit, importLimit and per collection limit control (#15405) (a7beeca)
🐛 Bug Fixes
- drizzle: use dynamic import for
typescriptto avoid dependency in production (#15545) (98a756c) - live-preview-vue: update build config to compile as esm (#14293) (60c65ed)
- next: drop support for Next.js versions with known CVEs, add canary 16.2.0 support (#15547) (2b3061a)
- next: suppress webpack "Critical dependency" warning in dynamicImport (#15534) (6158489)
- plugin-import-export: errors when import/export files were stored in a storage adapter such as S3 (#15441) (73a9650)
- ui: tab error badge not counting required array validation errors (#15563) (0ce6193)
- ui: folder view toggle button styles overridden due to equal specificity (#15544) (df42bec)
- ui: remove clearData call in dropzone drop handler (#10475) (b4e6761)
📝 Templates
⚙️ CI
🏡 Chores
- fix deprecation warning when running reinstall or clean:all monorepo script (#15535) (03bfaf4)
- drizzle: add logs indicating which migration statements are generated (#15374) (306974d)
🤝 Contributors
- Alessio Gravili (@AlessioGr)
- Patrik (@PatrikKozak)
- Sasha (@r1tsuu)
- Paul (@paulpopus)
- Daniel Waltz (@danielwaltz)
- Said Akhrarov (@akhrarovsaid)
v3.75.0
v3.75.0 (2026-02-05)
🚀 Features
- adds beforeNav and afterNav component slots (#15493) (f23a1df)
- next: pass full initReq context to server functions and dashboard widgets (#15427) (ce13e97)
🐛 Bug Fixes
- handle absolute paths correctly with tempFileDir upload option (#14436) (5ca9bd4)
- ungenerated image sizes should not store original URL (#15454) (fa1cd62)
- add safety check to redirects from external file URL uploads (#15458) (1041bb6)
- sass warning not hidden during webpack build (#15442) (dec0ea7)
- add collection property to auth documents, fixing multi tenancy access control issue (#15404) (d6aa6cc)
- graphql: blocks return null with select: true (#15464) (c2baef4)
- next: versions diff error when swapping blocks with relationship fields (#15478) (5ba0055)
- next: export SlugField from the client dir not rsc (#15461) (6c07f3b)
- next: sync modular dashboard widgets when server component re-renders (#15439) (5495b47)
- plugin-ecommerce: variant creation blocked by variants in trash (#15449) (3f01682)
- plugin-mcp: create and update resource tools now support point fields (#15381) (a28261d)
- ui: extra padding rendering in list view when no description exists (#15507) (3ff6be4)
- ui: remove slug field from rsc exports (#15480) (8f66035)
⚡ Performance
- next: avoid re-calculating permissions in some server functions, pass missing args (#15428) (9c8be5c)
📚 Documentation
🎨 Styles
🧪 Tests
- trash e2e URL regex too strict and blocks query parameters (#15498) (57e759c)
- additional admin e2e coverage for templates (#15477) (63d63be)
- reorganize helpers into shared/e2e/int subdirectories (#15479) (b901231)
- enable figma adapter testing (#15467) (f4920d8)
- improve script to reset tests (#15328) (399b579)
- richText fields serialization (#15465) (fc99048)
- update suites selected by default in runTestsWithSummary (#15463) (603897d)
- trash auth e2e tests have race condition on navigation (#15468) (9bd5123)
⚙️ CI
🏡 Chores
- fix eslint config lookup in formatting hooks (#15516) (38a88b3)
- remove duplicative agents.md, document ai tool compatibility (#15472) (6216f56)
- remove template lockfiles (#15494) (362bf2f)
- replace magic strings with centralized constants (#15475) (8422668)
- claude post-write hook causes error when IDE checks output and doesn't apply formatting correctly (#15445) (033b943)
- claude: add playwright mcp (#15447) (f15b9a3)
- plugin-multi-tenant: use beforeNav instead of beforeNavLinks for tenant selector (#15499) (5d7d544)
🤝 Contributors
- sanbrien (@sanbrien)
- Neha Prasad (@naaa760)
- Kendell (@kendelljoseph)
- Sean Zubrickas (@zubricks)
- Alessio Gravili (@AlessioGr)
- Patrik (@PatrikKozak)
- Jarrod Flesch (@JarrodMFlesch)
- Jake (@jacobsfletch)
- Kurt Tomlinson (@kurttomlinson)
- Colum Kelly (@columk1)
- German Jablonski (@GermanJablo)
- Jessica Rynkar (@JessRynkar)
- Elliot DeNolf (@denolfe)
v3.74.0
v3.74.0 (2026-01-30)
🚀 Features
- thread override access in doc level hooks (#15421) (85d5263)
- extend strictDraftTypes to all draft operations (#15292) (9239164)
- add support for custom UnpublishButton component (#15400) (94254da)
- storage-r2: client uploads using R2 multipart api (#14733) (5c38902)
- ui: allows opting out of popup closing logic (#15407) (fb2b602)
- ui: allows customizing Popup component portal className (#15406) (0f55464)
Override Access in Document-Level Hooks - Access the overrideAccess value inside collection and global hooks. Useful when hook logic needs to know whether access control was bypassed, such as when querying related documents up a hierarchy. #15421
export const Posts: CollectionConfig = {
slug: 'posts',
hooks: {
beforeChange: [
({ overrideAccess, req }) => {
if (overrideAccess) {
// Access control was bypassed
}
},
],
},
}Extended strictDraftTypes to All Operations - When strictDraftTypes: true is enabled, TypeScript now enforces draft type safety across all Local API operations (not just queries). The draft option is forbidden for collections/globals without drafts enabled, preventing silent runtime behavior where draft flags are ignored. #15292
import { buildConfig } from 'payload'
export default buildConfig({
typescript: {
strictDraftTypes: true, // Enables compile-time draft enforcement
},
// ...
})Custom UnpublishButton Component - Customize the UnpublishButton in collection and global configs, following the same pattern as PublishButton and SaveButton. Previously hardcoded. #15400
export const Posts: CollectionConfig = {
slug: 'posts',
admin: {
components: {
edit: {
UnpublishButton: '/components/CustomUnpublishButton',
},
},
},
}R2 Multipart Client Uploads (storage-r2) - Upload large files directly from the client using R2's multipart API. Files are split into smaller parts and uploaded separately, avoiding Cloudflare Worker memory limits. #14733
Popup Prevent Close Attribute (ui) - Add interactive elements inside popups without triggering close behavior by adding the data-popup-prevent-close attribute. #15407
<Popup>
<button data-popup-prevent-close onClick={handleClick}>
Click me without closing
</button>
</Popup>Popup Portal className (ui) - Customize the Popup component's portal container with the new portalClassName prop. #15406
<Popup portalClassName="my-custom-portal-class">
{/* content */}
</Popup>🐛 Bug Fixes
- isolate payload-preferences by auth collection (#15425) (2dc2e7c)
- traverseFields returning wrong parentPath dot notation for non-localised tabs (#15394) (99b051e)
- widgets and other features failing with transitive dependency imports (#15392) (5561799)
- replace deprecated scmp with crypto.timingSafeEqual (#15322) (2511c02)
- find afterRead hooks should behave like findByID (#15357) (3e27155)
- remove depth from count operation types (#15356) (dfc1600)
- publish button incorrectly shown after saving draft when access denied (#15319) (e833fe6)
- next: version view throws useLocale() server error (#15380) (2ce26fa)
- next: ensure query preset from url is applied (#15323) (592f404)
- next: ensure save preset button is not shown when there are no changes (#15320) (e9af097)
- plugin-cloud-storage: prevent infinite loop when cropping media (#15393) (345a9c7)
- plugin-cloud-storage: adds beforeChange hook to generate url on create (#15401) (d269d39)
- richtext-slate: localized indicator not displaying in label (#15412) (126f713)
- ui: use the formatAdminUrl function to generate unpublish url (#15375) (453e8a6)
- ui: restore default columns after clearing query preset (#15360) (029699d)
- ui: prevent globals crash with arrays fields when lock state user is undefined in handleDocumentLocking (#15259) (ea76ca0)
- ui: getEntityConfig did not respect globalSlug if collectionSlug is undefined (#15362) (b54059c)
📚 Documentation
- clarify beforeValidate and beforeChange hook data behavior (#15300) (ba9605e)
- add payload.logger.error usage guidelines to CLAUDE.md (#15398) (cdbfda2)
- updated redirects plugin integration docs and postgres connection troubleshooting (#15383) (9b2221e)
- improve select jsdocs and empty select docs (#15336) (4181a12)
🧪 Tests
📝 Templates
🔨 Build
⚙️ CI
- enable retries for int and unit tests (#15397) (8b21263)
- cache e2e prod preparation to run once instead of per-shard, various improvements and flake fixes (#15368) (2b3b9d5)
- automatically shard int tests (#15367) (1041b15)
- disable playwright tracing for first test runs, to improve performance and reduce flakes (#15337) (ab603c3)
- automatically shard e2e tests (#15366) (2f19f8f)
- add missing value property to outputs (#15338) (479b057)
- bump monorepo Node.js version from 23.11.0 to 24.13.0 (#15364) (d95c365)
- add linked PR feature to pop...
v3.73.0
v3.73.0 (2026-01-23)
🚀 Features
- next.js 16 support (#14456) (18ca83b)
- db-sqlite: add
busyTimeoutoption (#15317) (0c235c3) - db-sqlite: add WAL mode support (#15278) (3357aa6)
- drizzle: predefined migration for
blocksAsJSON: true(#15257) (1b7b13d) - live-preview: expose requestHandler arg to hooks (#15302) (4f3c31f)
- plugin-mcp: adds select API with CRUD tools (#15301) (268b33a)
- ui: use native useEffectEvent if available (#15304) (f0458fb)
Feature Details
🔥 Next.js 16 Support - Full compatibility with Next.js 16, including Turbopack HMR and build support. Requires Next.js >16.1.1-canary.35 or 16.2.0+. Templates will be updated after Next.js 16.2.0 is released. Stable Next.js 16.1.* is not supported, as we depend on fixes released in the Next.js canary. Support for cache components will follow in a future release #14456
WAL Mode Support (db-sqlite) - Enable SQLite Write-Ahead Logging for improved concurrent read/write performance. Configurable synchronous mode and journal size limit. #15278
Busy Timeout Option (db-sqlite) - Set maximum wait time in milliseconds when the database is locked, preventing SQLITE_BUSY errors in high-concurrency scenarios. #15317
Predefined Migration for blocksAsJSON (drizzle) - Migrate existing projects to use blocksAsJSON: true with a single command. Automatically updates your Payload config and generates the required migration. #15257
pnpm payload migrate:create --file @payloadcms/db-postgres/blocks-as-jsonRequest Handler in Live Preview Hooks (live-preview) - The useLivePreview hook (React and Vue) now accepts a requestHandler argument, allowing customization of data fetching. Useful when your frontend proxies requests or uses external middleware. #15302
Select API for MCP Tools (plugin-mcp) - Find, Create, and Update tools for Globals and Collections now support the select API, reducing token usage and aligning with Payload's existing query capabilities. #15301
Native useEffectEvent (ui) - Uses React's native useEffectEvent when available (React 19.2.0+), falling back to the existing polyfill for older versions. #15304
🐛 Bug Fixes
- select hasMany prevent duplicate values (#15218) (f4e8990)
- orderable fractional indexing case-sensitivity issue with PostgreSQL (#14867) (ef27ad9)
- find distinct sort on a different field (#15233) (e95f26d)
- conditional tabs breaking in Next.js 16 due to unstable tab id (#15270) (f5a7a00)
- correct previousValue and value in afterChange when using seo-plugin (#15253) (b6b6bab)
- db-mongodb: fix projection handling for relationship fields in GraphQL queries with select (#14850) (ace3447)
- drizzle: d1 sqlite IN querying of
idwhen any other join is present in the query (#15290) (4f5a9c2) - next: relationship fields with maxDepth: 0 show "Untitled - ID" in diff view (#15305) (65238c5)
- plugin-mcp: auto-detect basePath from Payload config routes (#15189) (dbc06f6)
- richtext-lexical: internal links render as href="#" in versions view (#15308) (ab4102c)
- sdk: correct return types with
select(#15289) (db40d7b) - ui: diff view columns have unequal widths for nested fields causing text misalignment (#15330) (db72a65)
- ui: upsertPreferences did not return preferences when creating new preferences (#15321) (94d5728)
- ui: pass the locale to reorder endpoint in orderable table (#14839) (d6bb3de)
📚 Documentation
- add jsdocs to query presets api (#15306) (3dbaed8)
- small md fix (#15299) (9a1eb77)
- remove csv as a language for code blocks (#15265) (259eac4)
📝 Templates
⚙️ CI
🏡 Chores
- removes never implemented readDrafts access arg on globals (#15297) (12b679f)
- export fractional indexing utilities (#15286) (02800b0)
🤝 Contributors
- Sasha (@r1tsuu)
- Patrik (@PatrikKozak)
- Alessio Gravili (@AlessioGr)
- Kendell (@kendelljoseph)
- David Murdoch (@dsm23)
- Rafau (@RafalFilipek)
- Aaron Claes (@AaronClaes)
- Jarrod Flesch (@JarrodMFlesch)
- German Jablonski (@GermanJablo)
- Elliot DeNolf (@denolfe)
- Jens Becker (@jhb-dev)
- Paul (@paulpopus)
- Vladyslav Prosolupov (@vladyslavprosolupov)
- Jessica Rynkar (@JessRynkar)