Skip to content

Releases: payloadcms/payload

v3.72.0

16 Jan 20:48
fbf48d2

Choose a tag to compare

v3.72.0 (2026-01-16)

🚀 Features

  • adds experimental option localizeStatus and allows unpublish per-locale functionality (#14667) (d77af00)
  • plugin-mcp: add depth parameter support to all MCP find resource tools (#14931) (c979fb3)

Localized Status (Experimental) - Each locale can now track and manage its own publication status independently. Publish or unpublish individual locales without affecting others, with locale-aware UI and version history. Requires enabling at both config and collection level. #14667

// payload.config.ts
export default buildConfig({
  experimental: {
    localizeStatus: true,
  },
  // ...
})

// collections/Posts.ts
export const Posts: CollectionConfig = {
  slug: 'posts',
  versions: {
    drafts: {
      localizeStatus: true,
    },
  },
}

⚠️ Migration Required: If you have existing version data, run the provided migration helper to convert _status fields from strings to locale objects. See PR #14667 for full migration guide.


Depth Parameter Support (plugin-mcp) - MCP resource tools now support a depth parameter (0-10) to control relationship population depth. Use depth: 0 for lightweight ID-only responses or higher values for fully populated relationship data. Significantly reduces token count when reading documents. #14931


🐛 Bug Fixes

  • thumbnailURL hook, virtually populate from thumbnail size (#15232) (beeb269)
    (4f452ac))
  • select in findByID with draft: true may return a wrong version (#14742) (49c9fa9)
  • use window.location.origin as fallback for API URL copy (#15220) (a46e3a2)
  • folder creation errors when collection uses translation function labels (#15216) (1a3aeb8)
  • correct image size URLs in beforeChange (#15214) (da12eed)
  • db-mongodb: hasMany relationship filtering with equals operator returns no results (#15204) (1756c0d)
  • deps: bump undici to 7.18.2 to mitigate chained decompression (#15221) (591f9d2)
  • plugin-ecommerce: issue with slug map ignoring variants and threading through cart data (#15234) (4b6529f)
  • plugin-ecommerce: translations not being mapped correctly (#15205) (3337403)
  • templates: prevent jobs run if secret unset (#15207) (8f50f83)
  • translations: correct Russian translations for UI states in general section (#14953) (454042e)
  • ui: truncates long JSON cells in list view (#9214) (6827978)

📚 Documentation

  • fix beforeChange field hook documentation to reflect actual behaviour (#14798) (533ae92)

🧪 Tests

  • fix race condition in language switcher helper (#15206) (f98d915)
  • add @payloadcms/storage-s3 clientUploads integration test suite (#15194) (4dce061)
  • fields with defaultValue ​​should not be overwritten in upsert (#15197) (c684c6b)

⚙️ CI

  • announce releases in discord general channel (#15201) (734453d)

🏡 Chores

🤝 Contributors

v3.71.1

13 Jan 21:38
178dd4c

Choose a tag to compare

v3.71.1 (2026-01-13)

🐛 Bug Fixes

  • ui: remove debug console.log from Form component (#15191) (5529f9b)

🤝 Contributors

v3.71.0

13 Jan 21:20
de88739

Choose a tag to compare

v3.71.0 (2026-01-13)

🚀 Features

  • supersedes option to job queue concurrency controls (#15179) (9aeb843)
  • add support for custom Status component in document controls (#11154) (ef863e6)
  • add exclusive concurrency controls for workflows and tasks (#15177) (35fe09d)
  • add bulkOperations.singleTransaction config option (#14387) (92da9fa)
  • add support for additional IANA timezones, custom UTC offsets and overriding the timezone field (#15120) (a8785ba)
  • ability to cancel current job from within workflow or task handlers (#15119) (1bd3146)
  • add typescript.strictDraftTypes flag for opt-in draft query type safety (#14388) (58faafd)
  • drizzle: include collection and global slugs in validation errors (#15147) (910d274)
  • plugin-ecommerce: new hooks, cart logic moved to the server and fixed several bugs (#15142) (dcd4030)
  • plugin-ecommerce: add new method refreshCart in useCart (#14765) (#14767) (529726e)
  • plugin-import-export: refactor plugin and add import functionality (#14782) (13e6035)
  • plugin-mcp: add draft parameter support to MCP find resource tool (#14924) (744a593)
  • plugin-mcp: adds tools that can find and update globals (#15091) (f6d9873)
  • plugin-nested-docs: add req parameter to GenerateURL and GenerateLabel types in nested docs (#14617) (6821a09)
  • plugin-redirects: add Japanese translations (#15080) (c5b57f7)
  • plugin-search: enables skipping of document syncing (#14928) (dfcf15c)
  • sdk: automatically fallback to generated types attempt (#15167) (ae50d39)
  • sdk: add proper error handling (#15148) (bfe2154)

Feature Details

Job Queue Concurrency Supersedes - Newer jobs can automatically delete older pending jobs with the same concurrency key. Enables "last queued wins" behavior for scenarios where only the latest state matters. #15179

concurrency: {
  key: ({ input }) => `generate:${input.documentId}`,
  exclusive: true,
  supersedes: true,  // Newer jobs delete older pending ones (not yet completed and did not start processing yet)
}

Exclusive Concurrency Controls - Prevents race conditions when multiple jobs operate on the same resource. Jobs with the same concurrency key will not run in parallel. Requires enableConcurrencyControl: true (will default to true in v4.0). #15177

export default buildConfig({
  jobs: {
    enableConcurrencyControl: true,
    workflows: [{
      slug: 'syncDocument',
      concurrency: ({ input }) => `sync:${input.documentId}`,
      handler: async ({ job }) => {
        // Only one job per documentId runs at a time
      }
    }]
  }
})

Job Cancellation from Handlers - Throw JobCancelledError from within a task or workflow handler to stop the job without retrying. #15119

Custom Status Component - Replace the Status section in document or global edit views without replacing the entire Edit view. Useful for custom locale publishing logic or additional status indicators. #11154

admin: {
  components: {
    edit: {
      Status: '/components/Status/index.tsx#Status',
    },
  },
},

Bulk Operations Single Transaction (db-mongodb) - Handle database transaction limitations when processing large numbers of documents in bulk operations. Useful for DocumentDB and Cosmos DB which have cursor limitations within transactions. #14387

Additional IANA Timezones & Custom UTC Offsets - Support for additional IANA timezone names via DateTimeFormat API validation, custom UTC offsets in ±HH:mm format, and the ability to override the timezone field configuration. #15120

{
  name: 'eventTime',
  type: 'date',
  timezone: {
    supportedTimezones: [
      { label: 'UTC+5:30 (India)', value: '+05:30' },
      { label: 'UTC-8 (Pacific)', value: '-08:00' },
      { label: 'UTC+0', value: '+00:00' },
    ],
  },
}

Override the timezone field:

{
  name: 'publishedAt',
  type: 'date',
  label: 'Published At',
  timezone: {
    override: ({ baseField }) => ({
      ...baseField,
      admin: {
        ...baseField.admin,
        disableListColumn: true, // Hide from list view columns
      },
    }),
  },
}

Strict Draft Types (typescript) - Opt-in strictDraftTypes flag for correct type safety when querying drafts. When enabled, find operations with draft: true will correctly type required fields as optional. Will become default in v4.0. #14388

export default buildConfig({
  typescript: {
    strictDraftTypes: true,  // defaults to false
  },
})

Validation Error Context (drizzle) - Unique constraint ValidationErrors now include data.collection or data.global for better error context when debugging. #15147

Server-Side Cart Logic (plugin-ecommerce) - Cart logic moved to the server with new REST API endpoints. New hooks: onLogin (merge guest cart with user cart), onLogout (clear session), clearSession, mergeCart, and refreshCart. Support for custom cart item matchers and MongoDB-style $inc operator for quantity changes. #15142

/**
 * Custom cart item matcher that includes fulfillment option.
 */
const fulfillmentCartItemMatcher: CartItemMatcher = ({ existingItem, newItem }) => {
  const existingProductID =
    typeof existingItem.product === 'object' ? existingItem.product.id : existingItem.product

  const existingVariantID =
    existingItem.variant && typeof existingItem.variant === 'object'
      ? existingItem.variant.id
      : existingItem.variant

  const productMatches = existingProductID === newItem.product

  const variantMatches = newItem.variant
    ? existingVariantID === newItem.variant
    : !existingVariantID

  const existingFulfillment = existingItem.fulfillment as string | undefined
  const newFulfillment = newItem.fulfillment as string | undefined
  const fulfillmentMatches = existingFulfillment === newFulfillment

  return productMatches && variantMatches && fulfillmentMatches
}

refreshCart Method (plugin-ecommerce) - Manually refresh cart state after direct modifications, allowing the UI to stay in sync without being blocked by addItem's uniqueness validation. #14767

Import Functionality (plugin-import-export) - Complete plugin refactor with new import functionality. Config is now per-collection with required collections array. Supports disabling import/export per collection and custom collection overrides. #14782 ⚠️ BREAKING CHANGE

importExportPlugin({
  overrideExportCollection: (collection) => {
    collection.admin.group = 'System'
    collection.upload.staticDir = path.resolve(dirname, 'uploads')
    return collection
  },
  overrideImportCollection: (collection) => {
    collection.admin.group = 'System'
    collection.upload.staticDir = path.resolve(dirname, 'uploads')
    return collection
  },
  collections: [
    {
      slug: 'posts',
      import: false, // disables import functionality, export enabled by default
    },
    {
      slug: 'pages',
      export: ({ collection }) => {
        collection.admin.group = 'System'
        collection.upload.staticDir = path.resolve(dirname, 'uploads')
        return co...
Read more

v3.70.0

05 Jan 20:07
9e9a45f

Choose a tag to compare

v3.70.0 (2026-01-05)

🚀 Features

  • support qs-esm sort arrays in REST API (#15065) (2ccf898)
  • richtext-lexical: adds docs page for lexical blocks, adds new lexical block component types and styles (#14971) (329115c)

Multi-Column REST API Sorting

Sort by multiple columns in the REST API using array syntax, aligning REST API behavior with the Local API. Previously only string-based sorting was supported. #15065

// Sort by multiple fields via REST API query string
// GET /api/posts?sort[0]=createdAt&sort[1]=-title

// Equivalent to Local API:
const posts = await payload.find({
  collection: 'posts',
  sort: ['createdAt', '-title'],
})

Lexical Blocks Documentation & Component Styles (richtext-lexical)

New documentation page for Lexical blocks with comprehensive examples showing usage and customization. Also introduces new block component types and styles for enhanced rich text editing. #14971

See the https://payloadcms.com/docs/rich-text/blocks for full details.

🐛 Bug Fixes

  • s3 plugin uploads files before validation (#14988) (502947b)
  • add beforeDocumentControls to globals generate importmap (#15036) (4468197)
  • warning during Next.js build "the request of a dependency is an expression" (#15007) (cd87ab4)
  • next: turbopack build version check not working for 16.1.1 canaries (#15005) (ab68a2f)
  • plugin-mcp: pin modelcontextprotocol/sdk dependency version to 1.24.0 (#15017) (1a9d665)
  • storage-*: allow prefix to always exist as a field via alwaysInsertFields flag (#14949) (23a8689)
  • ui: invalid sass imports to support windows - add Stylelint to prevent regression (#15028) (c66e953)

🧪 Tests

  • fix console logs not appearing on vitest (#15008) (e3879bf)
  • ensure vitest vscode extension env variables match CI (#15009) (f6248f9)
  • migrate from jest to vitest eslint plugin, remove remaining jest references (#14997) (418375c)

🏡 Chores

⚠️ BREAKING CHANGES

@payloadcms/storage-s3

Only users with custom hooks on S3 upload fields are affected — standard plugin usage is unchanged.

  • External upload process for the S3 plugin has moved from beforeChange to afterChange as a result of fix: s3 plugin uploads files before validation (#14988)

This change was necessary to ensure that files uploaded to S3 have passed all validations and been saved to the document, preventing orphaned external files.

🤝 Contributors

v3.69.0

19 Dec 21:43
7a4d37a

Choose a tag to compare

v3.69.0 (2025-12-19)

🚀 Features


Modular Dashboards with Widgets

Introduces customizable admin dashboards with draggable, resizable widgets. Build personalized dashboard layouts with full keyboard accessibility for reordering and resizing. Future updates will add widget fields (props) for configurable widgets and dashboard presets for sharing layouts. #13683

Screen.Recording.2025-11-28.at.17.13.14.mov

See the RFC discussion for background and roadmap.

AI Development Resources (templates)

All templates now ship with AGENTS.md and .cursor/rules/ directory for improved AI-assisted development with tools like Copilot and Cursor. #14889

See more about AGENTS.md

🐛 Bug Fixes

  • basePath not working properly with admin routes (#14967) (fa6b503)
  • get field by path for blocks (#14984) (519a3c6)
  • improves upload security for PDFs and SVGs (#14929) (61298c6)
  • missing range headers (#14887) (ec7c192)
  • next: status component incorrectly shows as published status on new documents saved as drafts when readVersions permissions are false (#14950) (394c024)
  • plugin-mcp: adds collection and strategy to user (#14981) (042d7eb)
  • plugin-multi-tenant: relationTo arrays inflating filterOptions where query size (#14944) (98b6791)
  • richtext-lexical: blocksFeature with relationship exposes other tenants (#14985) (3025377)
  • storage-s3: encode filename in generated URL (#14438) (86855e1)
  • ui: use portals for popup to prevent clipping, improve keyboard navigation (#14910) (af09932)

🛠 Refactors

📚 Documentation

🧪 Tests

🏡 Chores

  • storage-s3: add int tests for filename encoding (#14970) (ef710e3)

🤝 Contributors

v3.68.5

15 Dec 20:09
c20f6d4

Choose a tag to compare

v3.68.5 (2025-12-15)

🐛 Bug Fixes

🛠 Refactors

🤝 Contributors

v3.68.4

14 Dec 20:04
bb29c49

Choose a tag to compare

v3.68.4 (2025-12-14)

🐛 Bug Fixes

  • previousValue from hooks should be populated within lexical blocks (#14856) (bb1501e)
  • deps: enforce Next.js 15.4.10 (#14908) (7c675fa)
  • next: properly construct local req url (#14907) (471cd1b)
  • ui: show localized locale name for publish specific locale button (#14906) (848ea65)
  • ui: relationship add button unsafe permissions property access (#14903) (127e41a)

📚 Documentation

  • remove unused variable from custom field label translation (#14911) (77f96a4)
  • update collection access control reference link on collection config page (#14905) (3a1eb77)

⚠️ BREAKING CHANGES

🤝 Contributors

v3.68.3

11 Dec 22:40
be0e9b5

Choose a tag to compare

v3.68.3 (2025-12-11)

⚠️ Security Issue

A high-severity Denial of Service (CVE-2025-55184) and a medium-severity Source Code Exposure (CVE-2025-55183) affect React 19 and frameworks that use it, like Next.js.

Full details here: https://vercel.com/kb/bulletin/security-bulletin-cve-2025-55184-and-cve-2025-55183#how-to-upgrade-and-protect-your-next.js-app

While this is not a Payload vulnerability, it may affect any Payload project running on the affected versions of Next.js. Payload does not install any of these dependencies directly, it simply enforces their versions through its peer dependencies, which will only warn you of the version incompatibilities.

You will need to upgrade React and Next.js yourself in your own apps to the patched versions listed below in order to receive these updates.

Resolution

You are strongly encouraged to upgrade your own apps to the nearest patched versions of Next.js and deploy immediately.

Quick steps:

If using pnpm as your package manager, here's a one-liner:

pnpm add next@15.4.9

For a full breakdown of the vulnerable packages and their patched releases, see https://vercel.com/kb/bulletin/security-bulletin-cve-2025-55184-and-cve-2025-55183#how-to-upgrade-and-protect-your-next.js-app.

🐛 Bug Fixes

  • passes serverURL through to all formatAdminURL calls (#14869) (b82356b)

🤝 Contributors

v3.68.2

10 Dec 19:31
309ae51

Choose a tag to compare

v3.68.2 (2025-12-10)

🐛 Bug Fixes

  • next: use fileURLToPath for cross-platform path resolution (#14877) (e86098d)

Fixes Windows users getting ENOENT errors on dev startup.

  • next: unhandled error in renderListView server function when no user present (#14878) (10a8f0f)
  • next: merge user serverExternalPackages in withPayload (#14881) (dcecc46)

🛠 Refactors

  • replace generic Error('Unauthorized') with UnauthorizedError (#14879) (c9a8aa0)
  • next: remove unnecessary react hooks from DefaultTemplate rsc (#14876) (7a94cc1)

🏡 Chores

  • translations: improve Vietnamese translations (#14886) (25ef7d1)

🤝 Contributors

v3.68.1

09 Dec 21:22
92dcb83

Choose a tag to compare

v3.68.1 (2025-12-09)

🏡 Chores

  • do not suggest vulnerable Next.js release in turbopack build error message (#14873) (9e0f593)

🤝 Contributors