TL;DR
Payload CMS v3.18.0 introduces significant performance optimizations, reducing form state response size by up to 3x and improving overall response times. The release adds a new multi-tenant plugin for managing content across different tenants, enhances upload capabilities with server-side URL fetching, and improves field validation error messages. Several bug fixes address issues in rich text editors, form state handling, and UI components. TypeScript strictness has been improved across packages, and new examples for Remix and Astro integration have been added.
Highlight of the Release
- New multi-tenant plugin for managing content across different tenants
- Performance optimizations reducing form state response size by up to 3x
- Enhanced upload capabilities with server-side URL fetching
- Improved field validation error messages using field labels
- New examples for Remix and Astro integration with Payload Local API
- Support for JPEG XL image size calculation
Migration Guide
Multi-Tenant Plugin Configuration
If you're implementing the new multi-tenant plugin, you'll need to:
- Add a tenants collection to your Payload config
- Configure the plugin with your collections:
import type { Config } from './payload-types'
import { buildConfig } from 'payload'
import { multiTenantPlugin } from '@payloadcms/plugin-multi-tenant'
export default buildConfig({
plugins: [
multiTenantPlugin<Config>({
collections: {
pages: {},
// For collections that should behave like globals
navigation: {
isGlobal: true,
},
},
// Optional: function to determine if a user has access to all tenants
userHasAccessToAllTenants: (user) => isSuperAdmin(user),
}),
],
})
Server-Side URL Fetching Configuration
If you want to enable server-side URL fetching for uploads:
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
upload: {
pasteURL: {
allowList: [
{
hostname: 'payloadcms.com', // required
pathname: '',
port: '',
protocol: 'https', // defaults to https
search: ''
},
{
hostname: 'example.com',
pathname: '/images/*',
},
],
},
},
}
TypeScript Strictness
If you're extending Payload types or creating plugins, be aware that TypeScript strictness has been increased with:
strict: true
noUncheckedIndexedAccess: true
noImplicitOverride: true
You may need to update your code to comply with these stricter settings.
Upgrade Recommendations
This release contains significant performance improvements and new features without breaking changes, making it a recommended upgrade for all Payload users.
Who should upgrade immediately:
- Users experiencing performance issues with form state or list views
- Developers implementing multi-tenancy solutions
- Users who need to upload files from external URLs that currently face CORS issues
- Projects that would benefit from the TypeScript strictness improvements
Upgrade Steps:
-
Update your Payload dependencies:
npm install [email protected]
# or
yarn add [email protected]
# or
pnpm add [email protected]
-
If you're using any Payload plugins, update those as well to ensure compatibility.
-
Test your application thoroughly after upgrading, especially if you're using custom field hooks or have implemented custom form state handling.
-
If you want to take advantage of the new multi-tenant plugin, install it separately:
npm install @payloadcms/plugin-multi-tenant
The upgrade should be straightforward for most users as there are no breaking changes reported in this release.
Bug Fixes
Rich Text Editor Fixes
- Fixed an issue where JSX and HTML converters would output a linebreak if the Lexical editor was empty
- Fixed a bug where setting
hideInsertParagraphAtEnd to true did not hide the insert paragraph button
- Ensured inline blocks correctly store nested fields
- Fixed link and upload extra field drawers not rendering fields when collections had unrelated access control
Form State and Access Control Fixes
- Fixed missing
id argument in access.read function executed within form state
- Fixed incorrect setting of
data as doc in form state
- Fixed an issue where
basePath was not passed through if a method was overridden
- Fixed missing find collection versions REST endpoint
UI Improvements
- Fixed many bugs in the WhereBuilder relationship select menu
- Fixed placement issues with sonner toasts
- Added focus outline for sort column buttons for better accessibility
- Fixed hard-coded API paths by using
serverURL and routes.api
- Fixed custom block row labels not rendering properly
Other Fixes
- Fixed incorrect error logging in MongoDB adapter
- Fixed scheduled publish jobs not being deleted when documents are deleted
- Fixed
UpsertArgs not being exported in payload
- Fixed TypeScript strictness issues in various packages
New Features
Multi-Tenant Plugin
A new @payloadcms/plugin-multi-tenant package has been added to provide a standardized approach to multi-tenancy in Payload. This plugin:
- Adds a tenant selector to the sidebar above navigation links
- Adds a hidden tenant field to specified collections
- Adds an array field to the users collection for tenant assignment
- Combines access control with tenant-based permissions
- Filters list views based on the selected tenant
Enhanced Upload URL Fetching
The pasteURL feature for Upload fields now supports both client-side and server-side URL fetching:
- By default, Payload will attempt to fetch files client-side directly in the browser
- Server-side fetching can be enabled by configuring an
allowList of trusted domains
- A new
/api/:collectionSlug/paste-url endpoint is used for server-side fetching
- This solves CORS issues when fetching files from external domains
JPEG XL Support
Added support for calculating and displaying file sizes for JPEG XL images.
View Context Exposure
The context of the view being rendered is now exposed on the server, providing:
viewType - which view is being rendered (list, document, version, account, verify, reset)
documentSubViewType - which sub-view is active (api, livePreview, default, versions)
New Integration Examples
- Added example for using Remix with Payload Local API
- Added example for using Astro with Payload Local API
Security Updates
No specific security fixes were mentioned in this release.
Performance Improvements
Form State Optimization
The form state response size has been significantly reduced by up to 3x, improving overall response times:
- Removed
$undefined strings from properties like value, initialValue, and fieldSchema
- Removed unnecessary properties like empty
errorPaths arrays and empty customComponents objects
- Removed properties like
valid and passesCondition which only need to be returned if explicitly false
- Removed unused properties like
isSidebar that can be calculated during render
Deep Copy Optimizations
Several optimizations were made to reduce unnecessary deep copying of data:
- Removed unnecessary deep copying from field hook operations
- Moved deep copy logic from the
beforeValidate hook to the start of local API operations
- Ensured deep copying only runs for local API and not for GraphQL and REST operations
- Optimized the
sanitizeInternalFields function to reduce shallow copying
List View and UI Optimizations
- Improved list view rendering speed by ensuring the root layout does not re-render when navigating to list view
- Removed duplicative client CollectionConfig data being sent to the client for every table cell
- Optimized
getEntityConfig lookups by replacing array-based lookups with a map (O(n) to O(1))
- Replaced all instances of
validOperators.includes with validOperatorMap[] (O(n) to O(1))
- Reduced unnecessary shallow copying within operations
- Removed unnecessary
deleteMany call in deleteUserPreferences for auth-enabled collections
- Ensured unnecessary config translations are not sent to the client
- Removed undefined
minRows and maxRows values from being sent to the client
Rich Text Optimizations
- Ensured internal link nodes do not store URL field, and vice versa, reducing data redundancy
Impact Summary
Payload CMS v3.18.0 delivers substantial performance improvements that will benefit all users, with form state response sizes reduced by up to 3x. This translates to faster page loads, more responsive admin interfaces, and reduced server load.
The new multi-tenant plugin is a game-changer for projects requiring content segregation across different tenants, eliminating the need for custom implementations and providing a standardized approach to multi-tenancy. This is particularly valuable for SaaS applications, multi-site CMSes, and enterprise deployments.
Enhanced upload capabilities with server-side URL fetching solve a common pain point where users couldn't paste URLs from external domains due to CORS restrictions. The new allowlist approach provides both flexibility and security.
Improved field validation error messages using field labels instead of paths make the admin interface more user-friendly, especially for content editors working with complex nested fields.
TypeScript strictness improvements across packages will help catch potential bugs earlier in the development process and provide better type safety for developers extending Payload.
The addition of examples for Remix and Astro integration showcases Payload's versatility as a headless CMS that can work with modern frameworks through its Local API.
Overall, this release focuses on performance, developer experience, and expanding Payload's capabilities for complex use cases while maintaining backward compatibility.
Full Release Notes
🚀 Features
- adds support for both client-side and server-side remote URL uploads fetching (#10004) (38a06e7)
- support JPEG XL image size calculation (#10624) (6ebcbe4)
- adds multi-tenant plugin (#10447) (813e70b)
- examples: add example with Remix + Payload Local API (#10171) (5a9cf89)
- richtext-lexical: add jsx and html converters for tab nodes (#10565) (2e09da8)
- ui: exposes context of the view being rendered on the server (#10620) (e80d679)
🐛 Bug Fixes
- updates field validation error messages to use labels if applicable (#10601) (ad553e9)
- delete scheduled publish jobs when deleting documents (#10584) (05b9d94)
- UpsertArgs is not exported in payload (#9347) (7a392dd)
- form state read access control args (#10576) (05b03b2)
- missing find collection versions REST endpoint (#10573) (120735c)
- basePath was not passed through if method was overriden (#10562) (90e1843)
- db-mongodb: incorrect errors logging due to invalid logic in
handleError (#10575) (9043b10)
- payload-cloud: add ts strict mode and fix a couple of wrong runtime behaviors (#10570) (16ad7a6)
- plugin-seo: loosen some types to restore compatibility between minor versions (#10670) (9c29541)
- plugin-stripe: hooks did not use api key from plugin config (#10671) (d2f63dc)
- richtext-lexical: setting hideInsertParagraphAtEnd to true did not hide insert paragraph button (#10581) (8ab05b0)
- richtext-lexical: inline blocks did not store nested fields correctly (#10578) (61117ee)
- richtext-lexical: ensure jsx and html converters do not output linebreak if editor is empty (#10563) (df4af70)
- richtext-slate: link and upload extra field drawers did not render fields if collection has unrelated access control set (#10583) (ecf0572)
- ui: renders custom block row labels (#10686) (6c19579)
- ui: fixed many bugs in the WhereBuilder relationship select menu (#10553) (56667cd)
- ui: replace hard coded path to API with
serverURL and routes.api (#10618) (5a95237)
- ui: placement issue with sonner toasts (#10641) (f1cc8bd)
- ui: show outline on focus for sort column buttons (#9557) (818467d)
⚡ Performance
- list view table should not send duplicative client CollectionConfig to client (#10664) (823e223)
- ensure deepCopy in beforeValidate hook does not run unnecessarily for rest and graphQL API (#10666) (b69fe99)
- optimize getEntityConfig lookups (#10665) (c07c9e9)
- remove deepCopying in sanitizeJoinQuery, optimize flattenWhereToOperators (#10663) (91ed882)
- operations performance optimizations (#10609) (42382b6)
- reduce document data deepCopying in field hooks (#10610) (116fd99)
- do not send minRows and maxRows undefined values to client (#10600) (0a1cf7b)
- ensure unnecessary config translations are not sent to the client (#10524) (3fb6ac3)
- significantly reduce form state response size by up to 3x (#9388) (31ae27b)
- richtext-lexical: ensure internal link nodes do not store url field, and vice versa (#10564) (5d6c29f)
- ui: remove unnecessary deepCopy in reduceToSerializableFields (#10667) (a98a398)
- ui: speed up list view rendering, ensure root layout does not re-render when navigating to list view (#10607) (fafe37e)
📚 Documentation
🧪 Tests
📝 Templates
- form fields will now respect 'required' flag from config on website template (#10681) (2d70269)
- add cache tag to images so that they can be revalidated along with the page on website templates (#10647) (7d10e1b)
- bump for v3.17.1 (#10560) (592f02b)
📓 Examples
🔨 Build
- move larger scripts into tools dir in workspace (#10653) (f18ca9c)
⚙️ CI
- update canary script for tools dir (711febc)
- add multi-tenant plugin to publish list (076ffa2)
- disable integration tests retrying (#10615) (28b7c04)
- access sha in dispatch event (5ee36fc)
- dispatch event (f306785)
- scripts: publish-canary script always bump minor, more realistic [skip ci][skip lint] (4629784)
🏡 Chores
- set save-prefix='' for repo [skip ci] (ef4b8d9)
- run pnpm dev without dev - this improves error logging on init (#10669) (9215f03)
- adjust multi tenant version (#10640) (baad382)
- multi-tenant plugin updates (#10598) (0d47a5d)
- enable noUncheckedIndexedAccess in all packages except richtext-lexical (#10592) (d4039f2)
- enable noImplicitOverride in all packages (#10588) (d55b6a3)
- update mongodb-memory-server v9 -> v10 (#10556) (918bd72)
- make TypeScript strict in test folder. Simplify tsconfig (#10582) (a304dc4)
- make TypeScript strict by default in packages and 7 packages stricter (#10579) (085c1d0)
- db-mongodb: update mongoose and mongodb deps (#10644) (00cc10c)
- deps: upgrade various dependencies (#10657) (b6e9c3b)
- deps: bumps path-to-regexp (8c3f6e1)
- examples: bumps custom components example to latest and runs seed on init (#10661) (9f5ffed)
- richtext-lexical: add unit test that ensures lexical dependency checker is updated (#10561) (6a6ef8f)
- templates: make TypeScript strict in website template (#10587) (2ce3829)
🤝 Contributors