v3.74.0 (2026-01-30)
๐ Features
- thread override access in doc level hooks (#15421) (85d5263)
- extend strictDraftTypes to all draft operations (#15292) (9239164)
- add support for custom UnpublishButton component (#15400) (94254da)
- storage-r2: client uploads using R2 multipart api (#14733) (5c38902)
- ui: allows opting out of popup closing logic (#15407) (fb2b602)
- ui: allows customizing Popup component portal className (#15406) (0f55464)
Override Access in Document-Level Hooks - Access the overrideAccess value inside collection and global hooks. Useful when hook logic needs to know whether access control was bypassed, such as when querying related documents up a hierarchy. #15421
export const Posts: CollectionConfig = {
slug: 'posts',
hooks: {
beforeChange: [
({ overrideAccess, req }) => {
if (overrideAccess) {
// Access control was bypassed
}
},
],
},
}Extended strictDraftTypes to All Operations - When strictDraftTypes: true is enabled, TypeScript now enforces draft type safety across all Local API operations (not just queries). The draft option is forbidden for collections/globals without drafts enabled, preventing silent runtime behavior where draft flags are ignored. #15292
import { buildConfig } from 'payload'
export default buildConfig({
typescript: {
strictDraftTypes: true, // Enables compile-time draft enforcement
},
// ...
})โ ๏ธ Note: Generic collection slugs may require explicit type assertions when using draft options.
Custom UnpublishButton Component - Customize the UnpublishButton in collection and global configs, following the same pattern as PublishButton and SaveButton. Previously hardcoded. #15400
export const Posts: CollectionConfig = {
slug: 'posts',
admin: {
components: {
edit: {
UnpublishButton: '/components/CustomUnpublishButton',
},
},
},
}R2 Multipart Client Uploads (storage-r2) - Upload large files directly from the client using R2's multipart API. Files are split into smaller parts and uploaded separately, avoiding Cloudflare Worker memory limits. #14733
Popup Prevent Close Attribute (ui) - Add interactive elements inside popups without triggering close behavior by adding the data-popup-prevent-close attribute. #15407
<Popup>
<button data-popup-prevent-close onClick={handleClick}>
Click me without closing
</button>
</Popup>Popup Portal className (ui) - Customize the Popup component's portal container with the new portalClassName prop. #15406
<Popup portalClassName="my-custom-portal-class">
{/* content */}
</Popup>๐ Bug Fixes
- isolate payload-preferences by auth collection (#15425) (2dc2e7c)
- traverseFields returning wrong parentPath dot notation for non-localised tabs (#15394) (99b051e)
- widgets and other features failing with transitive dependency imports (#15392) (5561799)
- replace deprecated scmp with crypto.timingSafeEqual (#15322) (2511c02)
- find afterRead hooks should behave like findByID (#15357) (3e27155)
- remove depth from count operation types (#15356) (dfc1600)
- publish button incorrectly shown after saving draft when access denied (#15319) (e833fe6)
- next: version view throws useLocale() server error (#15380) (2ce26fa)
- next: ensure query preset from url is applied (#15323) (592f404)
- next: ensure save preset button is not shown when there are no changes (#15320) (e9af097)
- plugin-cloud-storage: prevent infinite loop when cropping media (#15393) (345a9c7)
- plugin-cloud-storage: adds beforeChange hook to generate url on create (#15401) (d269d39)
- richtext-slate: localized indicator not displaying in label (#15412) (126f713)
- ui: use the formatAdminUrl function to generate unpublish url (#15375) (453e8a6)
- ui: restore default columns after clearing query preset (#15360) (029699d)
- ui: prevent globals crash with arrays fields when lock state user is undefined in handleDocumentLocking (#15259) (ea76ca0)
- ui: getEntityConfig did not respect globalSlug if collectionSlug is undefined (#15362) (b54059c)
๐ Documentation
- clarify beforeValidate and beforeChange hook data behavior (#15300) (ba9605e)
- add payload.logger.error usage guidelines to CLAUDE.md (#15398) (cdbfda2)
- updated redirects plugin integration docs and postgres connection troubleshooting (#15383) (9b2221e)
- improve select jsdocs and empty select docs (#15336) (4181a12)
๐งช Tests
๐ Templates
๐จ Build
โ๏ธ CI
- enable retries for int and unit tests (#15397) (8b21263)
- cache e2e prod preparation to run once instead of per-shard, various improvements and flake fixes (#15368) (2b3b9d5)
- automatically shard int tests (#15367) (1041b15)
- disable playwright tracing for first test runs, to improve performance and reduce flakes (#15337) (ab603c3)
- automatically shard e2e tests (#15366) (2f19f8f)
- add missing value property to outputs (#15338) (479b057)
- bump monorepo Node.js version from 23.11.0 to 24.13.0 (#15364) (d95c365)
- add linked PR feature to popular-issues action (#15355) (614f13c)
๐ก Chores
- export getSafeFileName utility (#15424) (6b45fee)
- replace rmdirSync to rmSync (#15420) (17c0a3f)
- claude: add respond-to-discussion-features claude command (#15391) (1f7fe63)
- deps: bump form-data, tar, wrangler, and @opennextjs/cloudflare dependencies (#15435) (5875cd0)
- deps: bump tsx (#15363) (7043e3f)
- plugin-multi-tenant: removes basePath arg (#15202) (bd80784)
- ui: move unpublish actions into document controls dropdown (#15417) (99d61db)
๐ค Contributors
- Ricardo Tavares (@rjgtav)
- Patrik (@PatrikKozak)
- German Jablonski (@GermanJablo)
- Jessica Rynkar (@jessrynkar)
- Jarrod Flesch (@JarrodMFlesch)
- Tobias Odendahl (@tak-amboss)
- Paul (@paulpopus)
- Elliot DeNolf (@denolfe)
- Sean Zubrickas (@zubricks)
- Alessio Gravili (@AlessioGr)
- Georg Schelkshorn (@GeorgS)
- Muhammad Hassaan Farooq (@Muhammad-Hassaan-Farooq)
- RajeshKumar11 (@RajeshKumar11)