v3.70.0
payloadcms/payloadv3.70.0Jan 5, 2026by denolfe
AI Summary
Enhances the REST API with multi-column sorting and expands rich text capabilities, while fixing critical S3 upload security issues.
Key Highlights
- Multi-column sorting support in the REST API using array syntax.
- New documentation and component types for Lexical blocks.
- Critical security fix ensuring S3 uploads occur after validation.
Breaking Changes
- @payloadcms/storage-s3: External upload process for the S3 plugin has moved from `beforeChange` to `afterChange`
New Features
- Support qs-esm sort arrays in REST API
- Lexical blocks documentation and new component types and styles
- S3 plugin uploads files after validation to prevent orphaned files
- Fix for Next.js Turbopack build version check
- Migration from Jest to Vitest
Full Release Notes
## [v3.70.0](https://github.com/payloadcms/payload/compare/v3.69.0...v3.70.0) (2026-01-05)
### ๐ Features
* support qs-esm sort arrays in REST API ([#15065](https://github.com/payloadcms/payload/issues/15065)) ([2ccf898](https://github.com/payloadcms/payload/commit/2ccf898))
* **richtext-lexical:** adds docs page for lexical blocks, adds new lexical block component types and styles ([#14971](https://github.com/payloadcms/payload/issues/14971)) ([329115c](https://github.com/payloadcms/payload/commit/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](https://github.com/payloadcms/payload/pull/15065)
```ts
// 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. https://github.com/payloadcms/payload/pull/14971
See the https://payloadcms.com/docs/rich-text/blocks for full details.
### ๐ Bug Fixes
* s3 plugin uploads files before validation ([#14988](https://github.com/payloadcms/payload/issues/14988)) ([502947b](https://github.com/payloadcms/payload/commit/502947b))
* add beforeDocumentControls to globals generate importmap ([#15036](https://github.com/payloadcms/payload/issues/15036)) ([4468197](https://github.com/payloadcms/payload/commit/4468197))
* warning during Next.js build "the request of a dependency is an expression" ([#15007](https://github.com/payloadcms/payload/issues/15007)) ([cd87ab4](https://github.com/payloadcms/payload/commit/cd87ab4))
* **next:** turbopack build version check not working for 16.1.1 canaries ([#15005](https://github.com/payloadcms/payload/issues/15005)) ([ab68a2f](https://github.com/payloadcms/payload/commit/ab68a2f))
* **plugin-mcp:** pin modelcontextprotocol/sdk dependency version to 1.24.0 ([#15017](https://github.com/payloadcms/payload/issues/15017)) ([1a9d665](https://github.com/payloadcms/payload/commit/1a9d665))
* **storage-*:** allow prefix to always exist as a field via alwaysInsertFields flag ([#14949](https://github.com/payloadcms/payload/issues/14949)) ([23a8689](https://github.com/payloadcms/payload/commit/23a8689))
* **ui:** invalid sass imports to support windows - add Stylelint to prevent regression ([#15028](https://github.com/payloadcms/payload/issues/15028)) ([c66e953](https://github.com/payloadcms/payload/commit/c66e953))
### ๐งช Tests
* fix console logs not appearing on vitest ([#15008](https://github.com/payloadcms/payload/issues/15008)) ([e3879bf](https://github.com/payloadcms/payload/commit/e3879bf))
* ensure vitest vscode extension env variables match CI ([#15009](https://github.com/payloadcms/payload/issues/15009)) ([f6248f9](https://github.com/payloadcms/payload/commit/f6248f9))
* migrate from jest to vitest eslint plugin, remove remaining jest references ([#14997](https://github.com/payloadcms/payload/issues/14997)) ([418375c](https://github.com/payloadcms/payload/commit/418375c))
### ๐ก Chores
* adds comment in image component ([#14663](https://github.com/payloadcms/payload/issues/14663)) ([6459ebc](https://github.com/payloadcms/payload/commit/6459ebc))
* bump nodemailer to 7.0.12 (security) ([#15062](https://github.com/payloadcms/payload/issues/15062)) ([aa61b31](https://github.com/payloadcms/payload/commit/aa61b31))
* narrow down files affected by vitest lint rules ([#15000](https://github.com/payloadcms/payload/issues/15000)) ([b97a063](https://github.com/payloadcms/payload/commit/b97a063))
* **claude:** fix typo in claude skills access control advanced file ([#15060](https://github.com/payloadcms/payload/issues/15060)) ([5cbdca1](https://github.com/payloadcms/payload/commit/5cbdca1))
* **deps:** bump dnd-kit ([#15083](https://github.com/payloadcms/payload/issues/15083)) ([07c36a3](https://github.com/payloadcms/payload/commit/07c36a3))
### โ ๏ธ 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](https://github.com/payloadcms/payload/issues/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
- Alessio Gravili (@AlessioGr)
- Sean Zubrickas (@zubricks)
- Vincent Vu (@rubixvi)
- Paul (@paulpopus)
- Jessica Rynkar (@jessrynkar)
- Jonathan Elmgren (@jonathanelmgren)
- Patrikbjoh (@Patrikbjoh)
- Anders Semb Hermansen (@andershermansen)
- Riley Langbein (@rilrom)
- Elliot DeNolf (@denolfe)