v3.62.0
v3.62.0 (2025-10-30)
🚀 Features
Jobs Access Control
Adds role-based access control for job queue and cancel operations, allowing you to restrict who can manage background jobs in your application. Both operations now support overrideAccess parameter and respect custom access control functions defined in your jobs configuration. #14404
// Configure access control
jobs: {
access: {
queue: ({ req }) => req.user?.roles?.includes('admin'),
cancel: ({ req }) => req.user?.roles?.includes('admin'),
}
}
// Use in Local API
await payload.jobs.cancel({
where: { workflowSlug: { equals: 'sync' } },
overrideAccess: false,
req,
})Per-Field Timezone Configuration
Date fields can now have individual timezone settings, allowing different date fields to support their own list of supported timezones with custom default values. This enables more flexible date handling across your application. #14410
{
name: 'date',
type: 'date',
timezone: {
defaultTimezone: 'America/New_York',
supportedTimezones: [
{ label: 'New York', value: 'America/New_York' },
{ label: 'Los Angeles', value: 'America/Los_Angeles' },
{ label: 'London', value: 'Europe/London' },
],
},
}You can also enforce a specific timezone by specifying just one with a default value:
{
name: 'date',
type: 'date',
timezone: {
defaultTimezone: 'Europe/London',
supportedTimezones: [
{ label: 'London', value: 'Europe/London' },
],
},
}KV Storage Adapters
Introduces a new key-value storage system with multiple adapter options (Database, In-Memory, Redis) for enhanced data persistence and performance. This provides the foundation for the upcoming Realtime API and other features requiring fast key-value access. #9913
Access the KV store via payload.kv with the following interface:
interface KVAdapter {
/**
* Clears all entries in the store.
* @returns A promise that resolves once the store is cleared.
*/
clear(): Promise<void>
/**
* Deletes a value from the store by its key.
* @param key - The key to delete.
* @returns A promise that resolves once the key is deleted.
*/
delete(key: string): Promise<void>
/**
* Retrieves a value from the store by its key.
* @param key - The key to look up.
* @returns A promise that resolves to the value, or `null` if not found.
*/
get(key: string): Promise<KVStoreValue | null>
/**
* Checks if a key exists in the store.
* @param key - The key to check.
* @returns A promise that resolves to `true` if the key exists, otherwise `false`.
*/
has(key: string): Promise<boolean>
/**
* Retrieves all the keys in the store.
* @returns A promise that resolves to an array of keys.
*/
keys(): Promise<string[]>
/**
* Sets a value in the store with the given key.
* @param key - The key to associate with the value.
* @param value - The value to store.
* @returns A promise that resolves once the value is stored.
*/
set(key: string, value: KVStoreValue): Promise<void>
}Configure the adapter using the kv property:
buildConfig({
kv: adapter()
})Database KV adapter (default) - Uses your existing database with a hidden payload-kv collection:
import { databaseKVAdapter } from 'payload'
buildConfig({
kv: databaseKVAdapter({
kvCollectionOverrides: {
slug: 'custom-kv',
...(process.env.DEBUG === 'true' && {
admin: { hidden: false },
access: {},
}),
},
}),
})In Memory KV adapter - Fast memory-based storage for development:
import { inMemoryKVAdapter } from 'payload'
buildConfig({
kv: inMemoryKVAdapter(),
})Redis KV Adapter - Production-ready Redis integration:
pnpm add @payloadcms/kv-redisimport { redisKVAdapter } from '@payloadcms/kv-redis'
buildConfig({
kv: redisKVAdapter({
keyPrefix: "custom-prefix:", // defaults to 'payload-kv:'
redisURL: "redis://127.0.0.1:6379" // defaults to process.env.REDIS_URL
}),
})Configurable Toast Position
Toast notifications can now be positioned anywhere on the screen (top-left, top-center, top-right, bottom-left, bottom-center, bottom-right), giving you control over where important messages appear to your users. This is particularly useful for applications with large screens or specific UI layouts. #14405
The position configuration is a direct pass-through of the Sonner library's position options, with 'bottom-right' remaining the default.
Feature PRs
- add access control for jobs queue and cancel operations (#14404) (0a37edb)
- timezone config can now be added on a per field basis (#14410) (a3df837)
- add KV storage adapters (#9913) (ffb9a2e)
- make toast position configurable (#14405) (d5fab43)
- ui: export useQueue React hook (#14396) (612880d)
🐛 Bug Fixes
- globals with versions return _status field when access denied (#14406) (b766ae6)
- custom dashboard component causes runtime error on create-first-user and account views (#14393) (d5f4e72)
- claude: remove invalid frontmatter fields (#14411) (118d005)
- db-*:
findMigrationDirin projects withoutsrcfolder (#14381) (059185f) - db-mongodb: migration fails for cosmosDB (#14401) (10a640c)
- db-mongodb: type error with
prodMigrations(#14394) (8e5e23a) - db-mongodb: duplicate ids in sanitizeQueryValue (#11905) (36bb188)
- db-postgres: hasMany relationship/number/text fields inside blocks are incorrectly returned when using
select(#14399) (850cc38) - drizzle: number fields in generated schema with
defaultValue(#14365) (8996b35) - plugin-multi-tenant: remove unused syncTenants from useEffect deps (#14362) (906a3dc)
- richtext-lexical: do not run json.parse if value is undefined or null (#14385) (09a6140)
- richtext-lexical: prevent TS CodeBlocks from sharing Monaco model (useId) (#14351) (5caebd1)
- storage-*: update the client cache to use a map instead with cache keys per bucket config (#14267) (38f2e1f)
- ui: where builder crashing with invalid queries (#14342) (6c83046)
🛠 Refactors
- deprecate job queue depth property (#14402) (1341f69)
- ui: simplify ConfigProvider, improve useControllableState types and defaultValue fallback (#14409) (255320e)
⚙️ CI
- add claude as valid scope (560f2f3)
🤝 Contributors
- Dan Ribbens (@DanRibbens)
- Alessio Gravili (@AlessioGr)
- Paul (@paulpopus)
- Elliot DeNolf (@denolfe)
- Sasha (@r1tsuu)
- Patrik (@PatrikKozak)
- Ossaid Qadri (@imossaidqadri)
- Philipp Schneider (@philipp-tailor)
- Kholiavko Roman (@kholiavko-roman)
- Jacob Fletcher (@jacobsfletch)
- Séalan Cronin (@sealan)
- Jarrod Flesch (@JarrodMFlesch)
- Justus Blönnigen (@technicaldirector)