made with
payload
  • Showcases
  • Plugins
  • Creators
  • Tutorials
  • Templates
  • Videos

Not affiliated with PayloadCMS. Made by paul

  • Contact
  • Submit an entry
  • Releases
  • Stats
Back to Releases

v3.71.0

Version v3.71.0Minor Release
Released:

January 13, 2026

Type:

New features, backward compatible

GitHub:View Release
Download TarballDownload ZIP

Release Notes

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)
}
typescript

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
}
}]
}
})
typescript

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',
},
},
},
ts

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' },
],
},
}
ts

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
},
}),
},
}
ts

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
},
})
typescript

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
}
ts

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 collection
},
disableJobsQueue: true, // disable jobs queue for this collection only
},
],
debug: true,
})
ts

Draft Parameter for MCP Find (plugin-mcp) - Query draft/unpublished documents via the MCP plugin's find tool using the new draft boolean parameter. #14924 Globals Support (plugin-mcp) - New MCP tools to find and update globals. #15091 Request Parameter in Nested Docs (plugin-nested-docs) - req parameter added to generateURL and generateLabel functions for more flexibility (e.g., reading current locale). #14617 Skip Sync (plugin-search) - Conditionally skip syncing documents to the search index based on locale, document properties, or other criteria. #14928

skipSync: async ({ locale, doc, collectionSlug, req }) => {
if (!locale) return false
const tenant = await req.payload.findByID({
collection: 'tenants',
id: doc.tenant.id,
})
return !tenant.allowedLocales.includes(locale)
}
ts

Automatic Type Inference (sdk) - The SDK automatically uses your generated types via module augmentation—no need to manually pass GeneratedTypes. #15167

import { PayloadSDK } from '@payloadcms/sdk'
const sdk = new PayloadSDK({}) // Types inferred automatically from payload-types.ts
ts

Proper Error Handling (sdk) - The SDK now throws PayloadSDKError on failed API requests with status, errors, response, and message properties. #15148

import { PayloadSDKError } from '@payloadcms/sdk'
try {
await sdk.create({ collection: 'posts', data: { ... } })
} catch (err) {
if (err instanceof PayloadSDKError) {
console.log(err.status) // 400
console.log(err.errors) // [{ name: 'ValidationError', message: '...', data: {...} }]
}
}
ts

Japanese Translations (plugin-redirects) - Localized admin UI strings for Japanese users. #15080

🐛 Bug Fixes

  • wrong construction of urlToUse leads to false alert with logger.error (#15190) (ec6bba5)
  • adds missing transactions to login and logout operations (#15134) (dd494be)
  • set basePath from next config as env variable (#15154) (a8bfade)
  • throw error on empty relationTo array and allow disabling lockDocuments on all collections (#14871) (9521ec6)
  • add distinct validate to richtext field type definition (#15069) (d68e75a)
  • exclude files from being sent to the form-state action (#15174) (aaea133)
  • full image urls stored in DB (#15089) (d462f9b)
  • field schema map paths (#10852) (d288752)
  • custom OPTIONS endpoints are intercepted and cannot set custom CORS headers (#15153) (c103667)
  • upload drawer not loading data for uploads without files (#15150) (00fb6e8)
  • cannot read private member #headers error on Node.js 24 when using isolateObjectProperty (#15116) (e214deb)
  • db-mongodb: avoid unnecessary $lookup when a join field is not selected (#15149) (e39b1b5)
  • db-mongodb: exists operator on fields that have an array value in the db (#15152) (0afe200)
  • db-mongodb: find id field from flattened fields (#15110) (40081f4)
  • db-postgres: add filenameCompoundIndex baseIndexes for upload collections (#15182) (01f90c9)
  • db-postgres: localized and hasMany/polymorphic relationships/uploads inside blocks (#15095) (01e4412)
  • drizzle: unique field errors were not thrown as ValidationErrors (#15146) (43c19cb)
  • live-preview: remove payload import (#15160) (ec658a4)
  • plugin-cloud-storage: should persist external data returned by handleUpload (#15188) (7d80d21)
  • plugin-cloud-storage: prevent deleting original file when duplicating (#14961) (9d54267)
  • plugin-mcp: handle defaultDepth: 0 in api key authentication (#15014) (e0e058c)
  • plugin-multi-tenant: cannot clear selected tenant from dashboard view (#15141) (cd77e5d)
  • richtext-lexical,ui: make uploadNode default to alt user-defined values. (#15097) (3290b04)
  • storage-*: add range headers to storage adapters (#14890) (ef90811)
  • storage-s3: respect upload limits with client uploads (#15176) (4a6189e)
  • templates: fix mobile menu auth buttons overflow in ecommerce template (#15020) (6c1109f)
  • templates: improve cli detection in cloudflare template (#15098) (5d21ed1)
  • templates: add recommended serverExternalPackages to cloudflare (#15094) (0eed581)
  • translations: improve Japanese translations for naturalness and consistency (#15077) (88a901c)
  • ui: ensure up-to-date upload preview thumbnails in admin panel (#15029) (6f4b272)
  • ui: use optional chaining for potentiallyStalePath (#15185) (38cd67a)
  • ui: virtual fields with virtual: true show sort chevrons in list view (#15186) (33c3630)
  • ui: prevent text overlap in breadcrumbs (#15040) (0e27f19)
  • ui: avoid creating hasMany options during IME composition (#15086) (98bc876)
  • ui: duplicate document has unused serverURL dependency (#15178) (6d212b8)
  • ui: bulk upload error count (#15155) (0a46f4e)
  • ui: margins on small screens when using hideGutter in group (#15041) (22f94cd)
  • ui: preserve beforeInput/afterInput components in bulk edit (#14954) (c62dc2a)
  • ui: prevent wrong scroll target when adding rows in repeated array blocks (#15047) (f13a741)
  • ui: crop width and height inputs limited to -1 of max (#15101) (3a6d3bd)

⚡ Performance

  • optimized collection create when versions are enabled (#14885) (25813a7)
  • graphql: optimized join count when docs are not needed (#14872) (6025eff)

🛠 Refactors

  • clean up generated type resolution (#15163) (51d6cd2)
  • deprecate returning failed state from job queue handlers (#15102) (070ded7)

📚 Documentation

  • clarify available operations for beforeOperation hook (#14774) (8c59762)
  • update link for MDN docs on cookies in requests (#15075) (0859f1c)
  • updates custom upload component examples (#14678) (d3d8b4e)
  • add graphql multi-field sort note (#15123) (14ac061)
  • task failure docs (#15096) (81df3d6)

🧪 Tests

  • remove silly tests from accessComposition unit tests (#15158) (cc33129)
  • fixes flaky form state test (#15128) (251fc68)
  • adds assert helpers to the a11y suite (#15132) (daf8061)
  • running a11y test suite sequentially (#15130) (536582f)
  • update outdated hardcoded postgres connection strings (#15114) (7930f44)
  • fixes lexical block test flakes (#15113) (13edb8a)

⚙️ CI

  • remove website post release notification (#15133) (5ebda61)
  • deps: bumps setup-node version from 4 to 6 (#15122) (fe9119b)

🏡 Chores

  • deprecate skip parameter from db-adapters (#15183) (ea15f00)
  • add v4 comments for relationTo types (#15187) (d7351d5)
  • upgrade to pnpm 10 (#15137) (1c8d515)
  • refactor withPayload back to js (#15159) (44af1ad)
  • plugin-mcp: adds config.admin.user as an optional default userCollection (#14989) (cdabf79)
  • templates: replace arbitrary tailwind values with utilities in ecommerce template (#14654) (67cc734)

⚠️ BREAKING CHANGES

  • plugin-import-export: refactor plugin and add import functionality (#14782) (13e6035) This PR works to refactor the entire plugin so it's easier for us to work with it going forward and also implements import functionality.

🤝 Contributors

  • Sasha (@r1tsuu)
  • Jeffery To (@jefferyto)
  • Alessio Gravili (@AlessioGr)
  • tapartdev (@tapartdev)
  • Paul (@paulpopus)
  • Patrik (@PatrikKozak)
  • Elmo (@Spreizu)
  • Jarrod Flesch (@JarrodMFlesch)
  • German Jablonski (@GermanJablo)
  • Jessica Rynkar (@jessrynkar)
  • roboin (@Robot-Inventor)
  • Tobias Odendahl (@tak-amboss)
  • Rick Geersing (@RickGeersing)
  • Dan Ribbens (@DanRibbens)
  • Riley Langbein (@rilrom)
  • David Wensley (@wensleyio)
  • Bernhard Frick (@baer95)
  • Said Akhrarov (@akhrarovsaid)
  • Kendell (@kendelljoseph)
  • Jiayi Wang (@Jeromestein)
  • Jake (@jacobsfletch)
  • Jens Becker (@jhb-dev)
  • Sean Zubrickas (@zubricks)
  • Anton Timmermans (@atimmer)
  • Ricardo Tavares (@rjgtav)
  • Marcin Gierada (@teastudiopl)
  • Colum Kelly (@columk1)
  • Elliot DeNolf (@denolfe)
  • Jonathan Elmgren (@jonathanelmgren)
  • Thomas Coldwell (@thomas-coldwell)
Browse All ReleasesView on GitHub