github payloadcms/payload v3.60.0

2 days ago

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

    Screenshot 2025-10-14 at 11 43 37 AM

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. #14127

    multiTenantPlugin<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. #14119

    usersAccessResultOverride: ({
      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 supports disabledCollections and enabledCollections to control which collections appear in the upload drawer. Also refactors enabled relationships logic with cleaner useEnabledRelationships hook. #14111

  • Client-side markdown shortcuts & code blocks - Blocks with admin.jsx now support markdown shortcuts on the client (previously server-only). Includes pre-made CodeBlock component for use in BlocksFeature. Also fixes readOnly handling across nested fields. #13813

    Screenshot.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

  • richtext-lexical: add jsdoc to deprecated HTMLConverterFeature (#14193) (0b718bd)

๐Ÿ“š Documentation

  • revert sentry docs change (#14113) (8844eaf)
  • incorrect pg driver injection example in Sentry integration docs (#14088) (95dbaed)

๐Ÿงช Tests

  • allows running Jest tests through Test Explorer in VSCode and other improvements (#13751) (0d92a43)

โš™๏ธ 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

Don't miss a new payload release

NewReleases is sending notifications on new releases.