TL;DR
Payload CMS 3.20.0: Performance Boost & Enhanced Version Control
Payload CMS 3.20.0 brings significant performance improvements by optimizing field validations and introducing smarter form submission handling. The version comparison UI receives major enhancements with collapsible fields, customizable diff components, and the ability to show only modified fields. Other highlights include improved plugin stability, better localization publishing options, and numerous bug fixes across the admin UI and plugins.
Highlight of the Release
- Performance optimization by skipping field validations until form submission
- Enhanced version comparison UI with collapsible fields and option to show only modified fields
- Customizable version diff components through server-side configuration
- Option to set default locale publishing behavior (all locales or active locale only)
- Fixed field paths within hooks for more consistent behavior
Migration Guide
Upgrading to v3.20.0
No breaking changes are introduced in this release, making it a straightforward upgrade from previous v3.x versions.
For Custom Validation Functions
If you have custom validation functions that perform expensive operations (like database queries), consider updating them to use the new event parameter for better performance:
// Before
validate: async (value) => {
// Expensive operation runs on every keystroke
const result = await someExpensiveOperation(value)
return result ? true : 'Invalid value'
}
// After
validate: async (value, { event }) => {
if (event === 'onChange') {
// Simple validation during typing
return value ? true : 'Invalid value'
}
// Run expensive validation only on submit
const result = await someExpensiveOperation(value)
return result ? true : 'Invalid value'
}
For Localization Default Publish Option
If you want to change the default publish button behavior for localized content:
// In your Payload config
localization: {
defaultLocalePublishOption: 'active', // 'active' or 'all' (default)
locales: ['en', 'es', 'de'],
// other localization options...
}
Upgrade Recommendations
This release is recommended for all users due to the significant performance improvements and bug fixes.
- Priority: Medium
- Complexity: Low (no breaking changes)
- Timing: Can be upgraded at your convenience
The performance optimizations around field validations will be particularly beneficial for forms with many fields or complex validation logic. Users of the Multi-Tenant, Nested Docs, or Form Builder plugins should definitely upgrade to resolve the fixed issues.
To upgrade, run:
npm install @payloadcms/[email protected]
# or
yarn add @payloadcms/[email protected]
# or
pnpm add @payloadcms/[email protected]
If you're using any official plugins, consider upgrading those as well to ensure compatibility.
Bug Fixes
Core Fixes
- Field Paths: Fixed inconsistencies in field paths within hooks, ensuring proper formatting without unnecessary index prefixes.
- Lexical Rich Text: Fixed issue where afterRead hooks were not being properly awaited, potentially causing glitches with uploads, relationships, and link nodes.
- UI Improvements:
- Fixed blocksDrawer search state reset after closing
- Added title attribute to Logout button for tooltip display
- Fixed restore button's empty submenu in draft versions
- Fixed tab subfield permissions not being correctly extracted
Plugin Fixes
- Multi-Tenant Plugin:
- Now removes tenant cookie on logout
- Fixed validation error "The following field is invalid: Assigned Tenant"
- Prevents double redirect to globals when no tenant is selected
- Nested Docs Plugin:
- Now properly updates both draft and published child documents when parent document is updated
- Form Builder Plugin:
- Fixed type definitions for MessageField to be editor-agnostic
Other Fixes
- Fixed Estonian language support in translations
- Fixed Firefox selection preservation when using LexicalMenu
- Removed toString coercion inside getDocumentPermissions
- Fixed template issues with pnpm 10 compatibility
New Features
Enhanced Version Control UI
- Collapsible Fields in Version Diff: Fields like collapsible, group, array, blocks, and tabs can now be collapsed in the version comparison view, making it easier to navigate complex documents.
- Show Only Modified Fields: A new toggle allows users to focus only on fields that have changed between versions, reducing visual clutter.
- Customizable Diff Components: Developers can now customize version diff components through server-side configuration, including support for React Server Components.
Improved Localization Publishing
- Configurable Default Publish Option: Added
defaultLocalePublishOption to localization config, allowing developers to set whether the primary publish button should publish all locales (default) or only the active locale.
- Better User Experience: This provides a more intuitive workflow for teams that primarily work with individual locales.
Enhanced Blank Template
- Added a simple front-end landing page to the Blank template, providing a better out-of-the-box experience when deploying to platforms like Payload Cloud or Vercel.
- The front-end is entirely optional and can be removed if not needed.
Security Updates
No specific security fixes were mentioned in this release.
Performance Improvements
Optimized Field Validations
- Delayed Validation: Field validations now only run after form submission and on subsequent changes, rather than on every keystroke, significantly improving form responsiveness.
- Event-Based Validation: Added a new
event argument to validation functions, allowing developers to implement different validation logic for onChange vs onSubmit events.
- Filter Option Validation: Relationship and upload fields now only validate filter options on form submission, avoiding expensive database lookups during typing.
Example of Performance-Optimized Validation
[
{
name: 'text',
type: 'text',
validate: async (value, { event }) => {
if (event === 'onChange') {
// Do something highly performant here
return true
}
// Do something more expensive here (like DB queries)
return true
}
}
]
This approach is particularly beneficial for fields with async validations or those performing database queries, such as relationship and upload fields with filter options.
Impact Summary
Payload CMS 3.20.0 delivers substantial improvements to both performance and user experience. The most impactful change is the optimization of field validations, which now only run on form submission rather than on every keystroke. This results in significantly faster form interactions, especially for forms with many fields or complex validation logic.
The version comparison UI receives major enhancements with collapsible fields and the ability to show only modified fields, making it much easier to review changes in complex documents. Developers also gain the ability to customize version diff components through server-side configuration.
For teams working with localized content, the new option to set a default locale publishing behavior (all locales or active locale only) provides more flexibility in content workflows.
Several important bug fixes address issues in plugins (Multi-Tenant, Nested Docs, Form Builder) and core functionality, including proper field path handling within hooks and improved Lexical rich text editor stability.
Overall, this release focuses on performance, user experience improvements, and bug fixes without introducing breaking changes, making it a recommended upgrade for all Payload users.
Full Release Notes
🚀 Features
- allow publish and publish specific locale buttons to be swapped (#9438) (9b49741)
- allows fields to be collapsed in the version view diff (#8054) (828b3b7)
- ui: allows customizing version diff components, render versions ui on the server (#10815) (c562fbf)
- ui: toggle showing only modified fields in version diff view (#10807) (33ac13d)
🐛 Bug Fixes
- checks for localization to prevent publish button breaking (#10844) (9c31a52)
- field paths within hooks (#10638) (0acaf8a)
- next: remove toString coercion inside getDocumentPermissions (#10828) (9f9919d)
- plugin-form-builder: type of MessageField to object (#10792) (92e6beb)
- plugin-form-builder: type of MessageField to SerializedEditorState (#10789) (5603c1c)
- plugin-multi-tenant: issue #10740 - "The following field is invalid: Assigned Tenant" (#10764) (c1c64a0)
- plugin-multi-tenant: remove tenant cookie on logout (#10761) (22633a6)
- plugin-nested-docs: update draft and published child docs on resave (#10454) (7a39870)
- richtext-lexical: preserve selection in Firefox when using LexicalMenu (#10867) (0e5ff24)
- richtext-lexical: afterRead hooks were not awaited (#10747) (9f2bca1)
- translations: adds et to import file (#10823) (ffe8020)
- ui: adds title attribute to Logout button for tooltip (#10851) (989140b)
- ui: correctly reset blocksDrawer search state after close (#10847) (8a6d995)
- ui: adds prev value on form state validat functions (#10832) (a835518)
- ui: hide the restore button's empty submenu in a draft version (#10756) (0d81ff2)
- ui: include check for parent permissions in renderField (#10729) (8289588)
⚡ Performance
📚 Documentation
📝 Templates
⚙️ CI
- remove docker login, not functional for external contributors (eca4f47)
- update CODEOWNERS (d6ae07d)
- fix run e2e command (#10779) (344b231)
🏡 Chores
- improves routeError log safety (#10793) (ace7557)
- uncomment collectionSlugs array in fields test suite, for resetDB to work properly (#10778) (72a5c02)
- migrate outdated @payloadcms/next/utilities imports (#10777) (b9d3250)
- disable bun run test buttons if bun extension is installed (#10775) (03f7bdf)
- cpa: re-pin template versions (#10857) (c08f012)
- deps: upgrades react-diff-viewer-continued to v4.0.4 to suppress react 19 warnings and use ESM imports (#10834) (57f7218)
- templates: update missing changes in vercel website template (#10827) (c75c6ce)
- templates: fix eslint errors in vercel templates (#10768) (52f86c7)
- templates: fix: the contact page of the website template throws an error in live preview (#10785) (d6658b5)
- templates: fix eslint errors in plugin template (#10770) (b76401c)
🤝 Contributors