v3.60.0 (2025-10-16)
๐ Features
- accept multiple locales in fallbackLocale (#13822) (623a1b8)
- adds settingsMenu to admin navigation sidebar (#14139) (ee8b3cf)
- plugin-multi-tenant: allow collection access to be overridden via callback (#14127) (c40eec2)
- plugin-multi-tenant: allow hasMany on tenant field overrides (#14120) (fb93cd1)
- plugin-multi-tenant: user collection access overrides (#14119) (38b7a60)
- richtext-lexical: add collection filtering to UploadFeature, refactor relationship hooks (#14111) (6defba9)
- richtext-lexical: client-side block markdown shortcuts, code block (#13813) (07a1eff)
Localization
-
Multiple fallback locales -
fallbackLocale
now accepts an array of locales for queries and locale configs. Payload will check each locale in order until finding a value, eliminating the need for manual fallback handling. #13822/** Local API **/ await payload.findByID({ id, collection, locale: 'en', fallbackLocale: ['fr', 'es'], }) /** REST API **/ await fetch(`${baseURL}/api/${collectionSlug}?locale=en&fallbackLocale[]=fr&fallbackLocale[]=es`) /** GraphQL **/ await restClient.GRAPHQL_POST({ body, query: { locale: 'en', fallbackLocale: ['fr', 'es']}, }) /** Locale Configs **/ locales: [ { code: 'en', label: 'English', fallbackLocale: ['fr', 'es'], }, ]
Admin UI
-
Settings menu in navigation - New
admin.components.settingsMenu
config option adds a gear icon above the logout button. Click to open a popup menu with custom admin-level utilities and actions that don't fit into collection or global navigation. #14139
Multi-Tenant Plugin
-
Collection access overrides - New
accessResultOverride
callback allows modifying multi-tenant access control results per operation (read, create, update, delete, readVersions, unlock). Enables custom logic like allowing shared content across tenants. #14127multiTenantPlugin<ConfigType>({ collections: { posts: { accessResultOverride: async ({ accessResult, accessKey, req }) => { // here is where you can change the access result or return something entirely different if (accessKey === 'read') { return { or: [ { isShared: { equals: true, } }, accessResult ] } } else { return accessResult } } } } })
-
Multiple tenants per document - Tenant field overrides now support
hasMany
relationships, allowing documents to belong to multiple tenants. #14120 -
User collection access overrides - New
usersAccessResultOverride
callback enables customizing access control on the users collection, overriding default tenant privacy when needed. #14119usersAccessResultOverride: ({ accessKey: 'read', // 'create', 'read', 'update', 'delete', 'readVersions', 'unlock' accessResult: AccessResult, // the `AccessResult` type ...args, // AccessArgs }) => { // this is where you could adjust what gets returned here. if (accessKey === 'read') { return true // over simplified example } // default to returning the result from the plugin return accessResult }
Lexical Rich Text
-
Upload collection filtering -
UploadFeature
now supportsdisabledCollections
andenabledCollections
to control which collections appear in the upload drawer. Also refactors enabled relationships logic with cleaneruseEnabledRelationships
hook. #14111 -
Client-side markdown shortcuts & code blocks - Blocks with
admin.jsx
now support markdown shortcuts on the client (previously server-only). Includes pre-madeCodeBlock
component for use inBlocksFeature
. Also fixesreadOnly
handling across nested fields. #13813Screenshot.2025-10-01.at.10.14.54.mp4
๐ Bug Fixes
findDistinct
by explicit ID paths in relationships and virtual fields (#14215) (2b1b6ee)- hasMany / polymorphic relationships to custom number IDs (#14201) (a3b3865)
- hide fields with read: false in list view columns, filters, and groupBy (#14118) (bcd40b6)
- validate Point Field to -180 to 180 for longitude and -90 to 90 for latitude (#14206) (13a1d90)
- urls in upload sizes are not encoded (#14205) (747a049)
- restoring trashed drafts with empty required fields fails validation (#14186) (3317207)
- db-d1-sqlite: add missing
blocksAsJSON
property (#14103) (f14a38e) - db-mongodb: documents not showing in folders with
useJoinAggregations: false
(#14155) (e613a78) - db-mongodb: improve check for
ObjectId
(#14131) (32e2be1) - graphql: bump tsx version to get around esbuild vulnerability (#14207) (d7ec48f)
- next: custom views not overriding built-in single-segment routes (#14066) (691f810)
- plugin-multi-tenant: object reference mutations in addFilterOptionsToFields (#14150) (e62f1fe)
- richtext-lexical: state key collisions when multiple TextStateFeatures are registered (#14194) (f01a6ed)
- richtext-lexical: editor throws an error if OrderedList is registered but not UnorderedList or CheckList (#14149) (1fe75e0)
- richtext-lexical: editing a copied inline block also modifies the original (#14137) (5bacb38)
- richtext-lexical: correctly type field property of RenderLexical (#14141) (cd94f8e)
- richtext-lexical: improve type autocomplete and assignability for lexical nodes (#14112) (a46faf1)
- sdk:
pagination
is not passed to search params (#14126) (ee9f160) - storage-gcs: bump @google-cloud/storage for vulnerability (#14199) (1077aa7)
- storage-r2: uploads with prefix don't work, add
test/storage-r2
(#14132) (4fd4cb0) - templates: encoding and decoding slugs in website template (#14216) (cacf523)
- templates: ecommerce seeding issue (#14196) (d65b8c6)
๐ Refactors
๐ Documentation
- revert sentry docs change (#14113) (8844eaf)
- incorrect pg driver injection example in Sentry integration docs (#14088) (95dbaed)
๐งช Tests
โ๏ธ CI
- notify website repo on release [skip ci] (#14140) (a36e5d2)
- add website dispatch event, testing for releases [skip ci] (#14138) (b0d31ba)
- add missing labels to bug report template [skip ci] (#14134) (3069e5b)
- update generate-template-variations script to cover cloudflare template (#14105) (e78057b)
๐ก Chores
- better audit dependencies script (#14189) (f9e75a4)
- ignore agent local files [skip ci] (#14184) (7ecb5a0)
- drizzle: bump drizzle-orm and drizzle-kit for vulnerabilities (#14200) (4afa286)
- email-nodemailer: bump nodemailer dependencies to latest (#14121) (077c6f5)
๐ค Contributors
- Sasha (@r1tsuu)
- Patrik (@PatrikKozak)
- Paul (@paulpopus)
- Jessica Rynkar (@jessrynkar)
- German Jablonski (@GermanJablo)
- Elliot DeNolf (@denolfe)
- Elliott W (@elliott-w)
- Jarrod Flesch (@JarrodMFlesch)
- Alessio Gravili (@AlessioGr)
- Diogo Gaspar (@shadowoff09)