Skip to content

Conversation

@behilam
Copy link
Contributor

@behilam behilam commented Apr 6, 2024

Description

Adds a new translation: Esperanto

  • I have read and understand the CONTRIBUTING.md document in this repository.

Type of change

  • New feature (non-breaking change which adds functionality)

Checklist:

  • I have added tests that prove my fix is effective or that my feature works
  • Existing test suite passes locally with my changes
  • I have made corresponding changes to the documentation
@behilam behilam changed the title add Esperanto translation Apr 6, 2024
@behilam
Copy link
Contributor Author

behilam commented Apr 6, 2024

The failing tests should not be related to this PR. Should I do something else?

Edit: The tests are passing now in local with the new changes added to main.

@behilam
Copy link
Contributor Author

behilam commented May 11, 2024

Hi! I updated this PR with the latest changes from main. Is there something else needed?

@denolfe denolfe added keep Prevents from being marked stale or auto-closed. v2 labels Dec 3, 2024
jacobsfletch and others added 22 commits July 24, 2025 14:00
Supports grouping documents by specific fields within the list view.

For example, imagine having a "posts" collection with a "categories"
field. To report on each specific category, you'd traditionally filter
for each category, one at a time. This can be quite inefficient,
especially with large datasets.

Now, you can interact with all categories simultaneously, grouped by
distinct values.

Here is a simple demonstration:


https://github.com/user-attachments/assets/0dcd19d2-e983-47e6-9ea2-cfdd2424d8b5

Enable on any collection by setting the `admin.groupBy` property:

```ts
import type { CollectionConfig } from 'payload'

const MyCollection: CollectionConfig = {
  // ...
  admin: {
    groupBy: true
  }
}
```

This is currently marked as beta to gather feedback while we reach full
stability, and to leave room for API changes and other modifications.
Use at your own risk.

Note: when using `groupBy`, bulk editing is done group-by-group. In the
future we may support cross-group bulk editing.

Dependent on payloadcms#13102 (merged).

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210774523852467

---------

Co-authored-by: Paul Popus <paul@payloadcms.com>
Blocks container labels should match the Array and Tab labels. Uses same
styling approach as Array labels.

### Before
<img width="229" height="260" alt="CleanShot 2025-07-24 at 12 26 38"
src="https://github.com/user-attachments/assets/9c4eb7c5-3638-4b47-805b-1206f195f5eb"
/>

### After
<img width="245" height="259" alt="CleanShot 2025-07-24 at 12 27 00"
src="https://github.com/user-attachments/assets/c04933b4-226f-403b-9913-24ba00857aab"
/>
…payloadcms#13216)

## Problem:
In PR payloadcms#11887, a bug fix for `copyToLocale` was introduced to address
issues with copying content between locales in Postgres. However, an
incorrect algorithm was used, which removed all "id" properties from
documents being copied. This led to bug payloadcms#12536, where `copyToLocale`
would mistakenly delete the document in the source language, affecting
not only Postgres but any database.

## Cause and Solution:

When copying documents with localized arrays or blocks, Postgres throws
errors if there are two blocks with the same ID. This is why PR payloadcms#11887
removed all IDs from the document to avoid conflicts. However, this
removal was too broad and caused issues in cases where it was
unnecessary.


The correct solution should remove the IDs only in nested fields whose
ancestors are localized. The reasoning is as follows:
- When an array/block is **not localized** (`localized: false`), if it
contains localized fields, these fields share the same ID across
different locales.
- When an array/block **is localized** (`localized: true`), its
descendant fields cannot share the same ID across different locales if
Postgres is being used. This wouldn't be an issue if the table
containing localized blocks had a composite primary key of `locale +
id`. However, since the primary key is just `id`, we need to assign a
new ID for these fields.

This PR properly removes IDs **only for nested fields** whose ancestors
are localized.

Fixes payloadcms#12536

## Example:
### Before Fix:
```js
// Original document (en)
array: [{
  id: "123",
  text: { en: "English text" }
}]

// After copying to 'es' locale, a new ID was created instead of updating the existing item
array: [{
  id: "456",  // 🐛 New ID created!
  text: { es: "Spanish text" } // 🐛 'en' locale is missing
}]
```
### After fix:
```js
// After fix
array: [{
  id: "123",  // ✅ Same ID maintained
  text: {
    en: "English text",
    es: "Spanish text"  // ✅ Properly merged with existing item
  }
}]
```


## Additional fixes:

### TraverseFields

In the process of designing an appropriate solution, I detected a couple
of bugs in traverseFields that are also addressed in this PR.

### Fixed MongoDB Empty Array Handling

During testing, I discovered that MongoDB and PostgreSQL behave
differently when querying documents that don't exist in a specific
locale:
- PostgreSQL: Returns the document with data from the fallback locale
- MongoDB: Returns the document with empty arrays for localized fields

This difference caused `copyToLocale` to fail in MongoDB because the
merge algorithm only checked for `null` or `undefined` values, but not
empty arrays. When MongoDB returned `content: []` for a non-existent
locale, the algorithm would attempt to iterate over the empty array
instead of using the source locale's data.

### Move test e2e to int

The test introduced in payloadcms#11887 didn't catch the bug because our e2e suite
doesn't run on Postgres. I migrated the test to an integration test that
does run on Postgres and MongoDB.
### What?

This PR introduces complete trash (soft-delete) support. When a
collection is configured with `trash: true`, documents can now be
soft-deleted and restored via both the API and the admin panel.

```
import type { CollectionConfig } from 'payload'

const Posts: CollectionConfig = {
  slug: 'posts',
  trash: true, // <-- New collection config prop @default false
  fields: [
    {
      name: 'title',
      type: 'text',
    },
    // other fields...
  ],
}
```

### Why

Soft deletes allow developers and admins to safely remove documents
without losing data immediately. This enables workflows like reversible
deletions, trash views, and auditing—while preserving compatibility with
drafts, autosave, and version history.

### How?

#### Backend

- Adds new `trash: true` config option to collections.
- When enabled:
  - A `deletedAt` timestamp is conditionally injected into the schema.
- Soft deletion is performed by setting `deletedAt` instead of removing
the document from the database.
- Extends all relevant API operations (`find`, `findByID`, `update`,
`delete`, `versions`, etc.) to support a new `trash` param:
  - `trash: false` → excludes trashed documents (default)
  - `trash: true` → includes both trashed and non-trashed documents
- To query **only trashed** documents: use `trash: true` with a `where`
clause like `{ deletedAt: { exists: true } }`
- Enforces delete access control before allowing a soft delete via
update or updateByID.
- Disables version restoring on trashed documents (must be restored
first).

#### Admin Panel

- Adds a dedicated **Trash view**: `/collections/:collectionSlug/trash`
- Default delete action now soft-deletes documents when `trash: true` is
set.
- **Delete confirmation modal** includes a checkbox to permanently
delete instead.
- Trashed documents:
- Displays UI banner for better clarity of trashed document edit view vs
non-trashed document edit view
  - Render in a read-only edit view
  - Still allow access to **Preview**, **API**, and **Versions** tabs
- Updated Status component:
- Displays “Previously published” or “Previously a draft” for trashed
documents.
  - Disables status-changing actions when documents are in trash.
- Adds new **Restore** bulk action to clear the `deletedAt` timestamp.
- New `Restore` and `Permanently Delete` buttons for
single-trashed-document restore and permanent deletion.
- **Restore confirmation modal** includes a checkbox to restore as
`published`, defaults to `draft`.
- Adds **Empty Trash** and **Delete permanently** bulk actions.
  
#### Notes

- This feature is completely opt-in. Collections without trash: true
behave exactly as before.



https://github.com/user-attachments/assets/00b83f8a-0442-441e-a89e-d5dc1f49dd37
…oadcms#13265)

Previously, filtering by a polymorphic relationship inside an array /
group (unless the `name` is `version`) / tab caused `QueryError: The
following path cannot be queried:`.
…tion (payloadcms#13213)

When populating the selector it should populate it with assigned tenants
before fetching all tenants that a user has access to.

You may have "public" tenants and while a user may have _access_ to the
tenant, the selector should show the ones they are assigned to. Users
with full access are the ones that should be able to see the public ones
for editing.
🤖 Automated bump of templates for v3.49.0

Triggered by user: @denolfe

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Adds logic for svg+xml file type detection.

---------

Co-authored-by: Philipp Schneider <47689073+philipp-tailor@users.noreply.github.com>
Based from payloadcms#13276

Fixes payloadcms#7624

If an uploaded image has `.svg` ext, and the mimeType is read as
`application/xml` adjust the mimeType to `image/svg+xml`.

---------

Co-authored-by: Philipp Schneider <47689073+philipp-tailor@users.noreply.github.com>
…make paragraphs and lists match without CSS (payloadcms#13274)

Previously, the Lexical editor was using px, and the JSX converter was
using rem. payloadcms#12848 fixed the inconsistency by changing the editor to rem,
but it should have been the other way around, changing the JSX converter
to px.

You can see the latest explanation about why it should be 40px
[here](payloadcms#13130 (comment)).
In short, that's the default indentation all browsers use for lists.

This time I'm making sure to leave clear comments everywhere and a test
to avoid another regression.

Here is an image of what the e2e test looks like:

<img width="321" height="678" alt="image"
src="https://github.com/user-attachments/assets/8880c7cb-a954-4487-8377-aee17c06754c"
/>

The first part is the Lexical editor, the second is the JSX converter.

As you can see, the checkbox in JSX looks a little odd because it uses
an input checkbox (as opposed to a pseudo-element in the Lexical
editor). I thought about adding an inline style to move it slightly to
the left, but I found that browsers don't have a standard size for the
checkbox; it varies by browser and device.
That requires a little more thought; I'll address that in a future PR.

Fixes payloadcms#13130
Previously, `db.deleteMany` on postgres resulted in 2 roundtrips to the
database (find + delete with ids). This PR passes the where query
directly to the `deleteWhere` function, resulting in only one roundtrip
to the database (delete with where).

If the where query queries other tables (=> joins required), this falls
back to find + delete with ids. However, this is also more optimized
than before, as we now pass `select: { id: true }` to the findMany
query.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210871676349299
Respects join.type instead of hardcoding leftJoin
That property does not exist and was used in a previous, outdated
implementation of auto scheduling
Fixes payloadcms#13191

- Render a single html element for single error messages
- Preserve ul structure for multiple errors
- Updates tests to check for both cases
Looks like a merge resolution kept unused code. The same condition is
added a couple lines below this removal.
- bumps next.js from 15.3.2 to 15.4.4 in monorepo and templates. It's
important to run our tests against the latest Next.js version to
guarantee full compatibility.
- bumps playwright because of peer dependency conflict with next 15.4.4
- bumps react types because why not

https://nextjs.org/blog/next-15-4

As part of this upgrade, the functionality added by
payloadcms#11658 broke. This PR fixes it
by creating a wrapper around `React.isValidElemen`t that works for
Next.js 15.4.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210803039809808
…ayloadcms#13300)

### What?

- Fixed an issue where group-by enabled collections with `trash: true`
were not showing trashed documents in the collection’s trash view.
- Ensured that the `trash` query argument is properly passed to the
`findDistinct` call within `handleGroupBy`, allowing trashed documents
to be included in grouped list views.

### Why?

Previously, when viewing the trash view of a collection with both
**group-by** and **trash** enabled, trashed documents would not appear.
This was caused by the `trash` argument not being forwarded to
`findDistinct` in `handleGroupBy`, which resulted in empty or incorrect
group-by results.

### How?

- Passed the `trash` flag through all relevant `findDistinct` and `find`
calls in `handleGroupBy`.
…#13204)

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210561338171141
Ensures the browser uses fresh data after seeding by refreshing the
route and navigating when done.
Fixes typo in variable name within the relationship field component.

`disableFormModication` → `disableFormModification`
… components (payloadcms#13302)

Custom document tab components (server components) do not receive the
`user` prop, as the types suggest. This makes it difficult to wire up
conditional rendering based on the user. This is because tab conditions
don't receive a user argument either, forcing you to render the default
tab component yourself—but a custom component should not be needed for
this in the first place.

Now they both receive `req` alongside `user`, which is more closely
aligned with custom field components.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210906078627357
AlessioGr and others added 28 commits September 12, 2025 09:41
## Problem

When logging in with a `?redirect=`, the target page may crash. This
happens because the update from `SyncClientConfig` is applied in a
`useEffect`, which runs **_after_** the target page's client components
render. As a result, those components read the unauthenticated client
config on first render - even though they expect the authenticated one.

## Steps To Reproduce

1. logout
2. login with ?redirect=
3. document client component incorrectly still receives unauthenticated
client config on render
4. THEN the SyncClientConfig useEffect runs. Too late
5. Potential error (depending on the page, e.g. document view) -
document client component expects sth to be there, but it is not

## Solution

This PR replaces `SyncClientConfig` with a `RootPageConfigProvider`.
This new provider shadows the root layout’s `ConfigProvider` and ensures
the correct client config is available **_immediately_**.

It still updates the config using `useEffect` (the same
way`SyncClientConfig` did), but with one key difference:

- During the brief window between the redirect and the effect running,
it overrides the root layout’s config and provides the fresh,
authenticated config from the root page via the `RootConfigContext`.

This guarantees that client components on the target page receive the
correct config on first render, preventing errors caused by reading the
outdated unauthenticated config.

## Additional change - get rid of `UnsanitizedClientConfig` and
`sanitizeClientConfig`

Those functions added unnecessary complexity, just to build the
blocksMap. I removed those and perform the building of the `blocksMap`
server-side - directly in `createClientConfig`.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211334752795621
The information about the block field and the blocks themselves wasn't
separated in the documentation, and in fact, there were times when they
were confused and incorrectly mixed.

This caused confusion among our users.

This PR closes an issue, a discussion, and another PR that arose because
it wasn't clear that the `blockName` could be programmatically modified
with the block's content. This is possible through
`admin.components.Label`, which isn't the best name, as it changes the
`label` and the `name` of the block, two different concepts.

I've added a section to the documentation that I think will make all of
this much clearer.

Before:

<img width="266" height="362" alt="image"
src="https://github.com/user-attachments/assets/3b5c95f0-2cdb-4995-a25d-9984f9ab54cb"
/>

After:

<img width="266" height="383" alt="image"
src="https://github.com/user-attachments/assets/99936f64-326f-4d69-a464-626d7f53fa08"
/>


Replaces payloadcms#12135
Fixes payloadcms#12112 & addresses
payloadcms#4648

---------

Co-authored-by: Said Akhrarov <36972061+akhrarovsaid@users.noreply.github.com>
…3789)

### What?

Opening a document with an expired lock and richtext caused the richtext
to be read-only.

### Why?

The changes in payloadcms#13579 made the richtext read only if isLocked is set.
But the server side implementation of isLocked did not consider expired
locks.

### How?

Update the server-side getIsLocked to also consider expired locks by not
loading them.
Redirecting from login to any non-auth collection crashes the page with
the following error:

```
Cannot read properties of null (reading 'fields')
```

TL;DR: the page-level config context was threading stale methods from
the root config provider.

#### Background

The client config is now gated behind authentication as of payloadcms#13714, so it
changes based on authentication status. If the root layout is mounted
before authentication, it puts the unauthenticated client config into
state for the entire app to consume.

On login, the root layout does not re-render, so the page itself needs
to generate a fresh client config and sync it up.

This leads to race conditions, however, where if the login page included
a `?redirect=` param, the redirect would take place _before_ the
page-level client config could sync to the layout, and ultimately crash
the page. This was addressed in payloadcms#13786.

While this fixed redirects to the "users" collection, this collection is
_already_ included in the client config (soon to be omitted by payloadcms#13785).
So if you redirect to any other collection, the above error occurs.

#### Problem

The page-level config context is only overriding the `config` property,
keeping stale methods from the root config provider. This means calling
`getEntityConfig` during this moment in the time would reference the
stale config, although `config` itself would be fresh.

#### Solution

Wrap the page with an entirely new context provider. Do not thread
inherited methods from the root provider, this way all new methods get
instantiated using the fresh config.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211332845301596
…eview messages (payloadcms#13793)

### What?

Fix two live preview issues affecting client-side navigation:
1. Stale preview data leaking between pages using `useLivePreview`.
2. Erroneous fetches to `/api/undefined` and incorrect content rendering
when preview events lack slugs.

### Why?

The live-preview module cached merged preview data globally, which
persisted across route changes, causing a new page to render with the
previous page’s data.

The client attempted to merge when preview events didn’t specify
collectionSlug or globalSlug, producing an endpoint of undefined and
triggering requests to /api/undefined, sometimes overwriting state with
mismatched content.

### How?

Clear the internal cache at the time of `subscribe()` so each page using
`useLivePreview` starts from a clean slate.

In `handleMessage`, only call `mergeData` when `collectionSlug` or
`globalSlug` is present; otherwise return `initialData` and perform no
request.

Fixes payloadcms#13792
Follow-up to payloadcms#13714.

Fully sanitizes the unauthenticated client config to exclude much of the
users collection, including fields, etc. These are not required of the
login flow and are now completely omitted along with other unnecessary
properties.

This is closely aligned with the goals of the original PR, and as an
added bonus, makes the config _even smaller_ than it already was for
unauthenticated users.

Needs payloadcms#13790.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211332845301588
payloadcms#13796)

Similar spirit as payloadcms#13714.

Permissions are embedded into the page response, exposing some field
names to unauthenticated users.

For example, when setting `read: () => false` on a field, that field's
name is now included in the response due to its presence in the
permissions object.

We now search the HTML source directly in the test, similar to "view
source" in the browser, which will be much effective at preventing
regression going forward.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211347942663256
…yloadcms#13728)

### What?

Fixes a typo in the description of query presets. 

### Why?

Grammar
…ayloadcms#13748)

### What?
Updated a few words to better represent what they mean in Dutch.

### Why?
The words that are used now are synonyms but have the wrong meaning in
the context of the UI.

### How?
Clear  - Duidelijk  - This means like "This explanation was very clear"
Changed it to "Wissen", which is to clear filters.

Close - Dichtbij - This means close by, like "This drive is just around
to corner, it is close by"
Changed it to "Sluiten", which is to close for example a dialog
… "Cannot read properties of undefined" (payloadcms#13800)

Fixes payloadcms#12924

### What?

If an access check request resulted in no access to a doc, then the
sanitizer will remove even the `collections` field from the sanitized
permissions. This throws a server error when the `collections` field is
attempted to be accessed with some collection slug. The error is "Cannot
read properties of undefined".

### Why?

An example of how this might come about:
I am using multi-tenancy. I have some conditions about which users can
see other user's information (do they share any tenants). One workflow
is such that a "super admin" (user A) of a tenant removes an "admin"
(user B) inside their tenant. This is done by removing the tenant from
the user's list. After user A's update succeeds, payload defaults to
trying to reload user B's page. However, the two no longer share a
tenant. The access controls correctly evaluate that user A should no
longer be able to see/update/etc user B, but there is a bug in how the
objects are handled (see below).

The sanitizer removes the .collections field from the
sanitizedPermissions, so we get a server error about the key missing
"Cannot read properties of undefined (reading 'users')" in my case.

### How?

Instead of immediately keying into the sanitizedPermissions, we see if
the collections exist. If not, we make sure to return a well-defined
object with no permissions.

Before:

```ts
return sanitizedPermissions.collections![config.slug]!
```

After:

```ts
const collectionPermissions = sanitizedPermissions?.collections?.[config.slug]
return collectionPermissions ?? { fields: {} }
```

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
…agination (payloadcms#13805)

When searching and the data is still being fetched, users see "no
options" until the data is populated, this is very confusing especially
if the loading time could take more seconds, user would start clearing
input and type again.

This PR shows the loading indicator during search and pagination by
setting `isLoading` to true immediately when search begins and setting
it to false when data fetching completes

**Before**


https://github.com/user-attachments/assets/86367207-31f6-40f0-bd98-c9e885ecd0dd



**After**



https://github.com/user-attachments/assets/202d2c05-61a6-4e3a-9973-65b302b4495a
…zed fields (payloadcms#13794)

Fixes payloadcms#13756 

The findByID endpoint, by default, expects the data for localized fields
to be an object, values mapped to the locale. This is not the case for
client-side live preview, as we send already-flattened data to the
findByID endpoint.

For localized fields where the value is an object (richText/json/group),
the afterRead hook handler would attempt to flatten the field value,
even though it was already flattened.

## Solution

The solution is to expose a `flattenLocales` arg to the findByID
endpoint (default: true) and pass `flattenLocales: false` from the
client-side live preview request handler.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211334752795627
…re reliable job system crons (payloadcms#13564)

## New jobs:handle-schedules bin script

Similarly to `payload jobs:run`, this PR adds a new
`jobs:handle-schedules` bin script which only handles scheduling.

## Allows jobs:run bin script to handle scheduling

Similarly to how [payload
autoRun](https://payloadcms.com/docs/jobs-queue/queues#cron-jobs)
handles both running and scheduling jobs by default, you can now set the
`payload jobs:run` bin script to also handle scheduling. This is opt-in:

```sh
pnpm payload jobs:run --cron "*/5 * * * *" --queue myQueue --handle-schedules # This will both schedule jobs according to the configuration and run them
```

## Cron schedules for all bin scripts

Previously, only the `payload jobs:run` bin script accepted a cron flag.
The `payload jobs:handle-schedules` would have required the same logic
to also handle a cron flag.

Instead of opting for this duplicative logic, I'm now handling cron
logic before we determine which script to run. This means: it's simpler
and requires less duplicative code.

**This allows all other bin scripts (including custom ones) to use the
`--cron` flag**, enabling cool use-cases like scheduling your own custom
scripts - no additional config required!

Example:

```sh
pnpm payload run ./myScript.ts --cron "0 * * * *"
```

Video Example:


https://github.com/user-attachments/assets/4ded738d-2ef9-43ea-8136-f47f913a7ba8

## More reliable job system crons

When using autorun or `--cron`, if one cron run takes longer than the
cron interval, the second cron would run before the first one finishes.

This can be especially dangerous when running jobs using a bin script,
potentially causing race conditions, as the first cron run will take
longer due to payload initialization overhead (only for first cron run,
consecutive ones use cached payload). Now, consecutive cron runs will
wait for the first one to finish by using the `{ protect: true }`
property of Croner.

This change will affect both autorun and bin scripts.

## Cleanup

- Centralized payload instance cleanup (payload.destroy()) for all bin
scripts
- The `getPayload` function arguments were not properly typed. Arguments
like `disableOnInit: true` are already supported, but the type did not
reflect that. This simplifies the type and makes it more accurate.

## Fixes

- `allQueues` argument for `payload jobs:run` was not respected


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211124797199077
…ter (payloadcms#13809)

### What?

Remove `select` parameter from database operations in update operation
during version creation to fix malformed version data.

### Why?

The `select` parameter was being passed to `saveVersion()` in update
operations, causing versions to only contain selected fields

### How?

- Removed `select` parameter from `payload.db.updateOne()` calls in
update operations
- Removed `select` parameter from `saveVersion()` call in update
operation


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211334352350013
…#12918)

Follow-up to payloadcms#12893.

### What?

Removes cases where submitting a document as a draft in a collection
with versioning enabled would cause publishing validation errors to be
displayed on further document form changes. An example case is when
saving the draft failed due to a `beforeChange` hook throwing an
`APIError`.

### How

The behavior change is that the form state is marked as un-submitted
post a submit failure as a draft. The form not being considered as
submitted results in `packages/ui/src/views/Edit/index.tsx` to use
`skipValidation: true`.

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
Fixes payloadcms#12286. Supersedes
payloadcms#12290.

As of
[v3.35.0](https://github.com/payloadcms/payload/releases/tag/v3.35.0),
you are no longer able to directly pass a `path` prop to a custom field
component.

For example:

```tsx
'use client'
import React from 'react'
import { TextField } from '@payloadcms/ui'
import type { TextFieldClientComponent } from 'payload'

export const MyCustomField: TextFieldClientComponent = (props) => {
  return (
    <TextField
      {...props}
      path="path.to.some.other.field" // This will not be respected, because this field's context takes precedence
    />
  )
}
```

This was introduced in payloadcms#11973 where we began passing a new
`potentiallyStalePath` arg to the `useField` hook that takes the path
from context as priority. This change was necessary in order to fix
stale paths during row manipulation while the server is processing.

To ensure field components respect your custom path, you need to wrap
your components with their own `FieldPathContext`:

```tsx
'use client'
import React from 'react'
import { TextField, FieldPathContext } from '@payloadcms/ui'
import type { TextFieldClientComponent } from 'payload'

export const MyCustomField: TextFieldClientComponent = (props) => {
  return (
    <FieldPathContext path="path.to.some.other.field">
      <TextField {...props} />
    </FieldPathContext>
  )
}
```

It's possible we can remove this in the future. I explored this in
payloadcms#12290, but it may require some more substantial changes in
architecture. These exports are labeled experimental to allow for any
potential changes in behavior that we may need to make in the future.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210533177582945
…s#13269)

### What?
Passes the same args provided to a task's `shouldRestore` function to
both the `onSuccess` & `onFail` callbacks

### Why?
Currently `onSuccess` and `onFail` are quite useless without any
context, this will allow for a wider range of functionality:

- Checking if it's the last failure
- Access to the task `input`
- Access to `req` to allow logging, email notifications, etc.

### How?
1. Created a new `TaskCallbackArgs` type, which replicates the args of
the `ShouldRestoreFn` type.
2. Add a `TaskCallbackFn` type
3. Update the function calls of both `onSuccess` and `onFail`.

### Questions
- I wasn't sure about the typing of `input` – I can see `input: input!`
being used elsewhere for task errors so I replicated that.
- Same for `taskStatus`, I added a type check but I'm not sure if this
is the right approach (what would scenario would result in a `null`
value?). Should `TaskCallbackArgs['taskStatus']` be typed to allow for
`null` values?

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
…ds (payloadcms#13807)

### What?

Skip field validation when trashing documents with empty required
fields.

### Why?

When trashing a document that was saved as a draft with empty required
fields, Payload would run full validation and fail with "The following
fields are invalid" errors. This happened because trash operations were
treated as regular updates that require full field validation, even
though trashing is just a metadata change (setting `deletedAt`) and
shouldn't be blocked by content validation issues.
   
### How?

- Modified `skipValidation` logic in `updateDocument()` to skip
validation when `deletedAt` is being set in the update data

Fixes payloadcms#13706
… types (payloadcms#13820)

### What?

Fixes crash when virtual fields use field types not supported by the
WhereBuilder (e.g. 'group' fields).

<img width="1336" height="459" alt="Screenshot 2025-09-16 at 11 00
30 AM"
src="https://github.com/user-attachments/assets/3adbb507-3033-4f52-b7cc-3c5bad74900b"
/>

### Why?

Users reported a crash with error "Cannot read properties of undefined
(reading 'operators')" when entering collection list views with virtual
fields that have `type: 'group'`. The issue occurred because:
- Virtual fields can use any field type including 'group'
- The `fieldTypes` object in WhereBuilder only supports specific field
types (text, number, select, etc.)
- Code tried to access `fieldTypes[field.type].operators` when
`field.type` was 'group', causing undefined access

### How?

- Refactored virtual field handling to use a unified processing flow
instead of separate early returns
- Virtual fields now set the `pathPrefix` to their virtual path and
continue through normal field validation

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211315667956059

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
…#13819)

Previously, sorting like:
`sort: 'relationship.anotherRelationship.title'` (and more nested)
didn't work with the MongoDB adapter, this PR fixes this.
…or (payloadcms#13825)

When using server-side live preview across domains, the initial
`postMessage` to the iframe throws the following error:

```txt
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://your-frontend.com') does not match the recipient window's origin ('https://your-backend.com').
```

This error is misleading, however, as it's thrown even when the iframe's
source exactly matches the post message's `targetOrigin`.

For example:

```ts
recipient.postMessage(message, targetOrigin)
```

The problem is that the initial message is sent before the iframe is
ready to receive it, resulting in the parent window posting to itself.
This is not a problem when the front-end is running on the same server,
but if the recipient changes while initializing, the target origin will
be mismatched.

Worth noting that this is not an issue with client-side live preview.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211376499297320
…alVersions` and handle it properly (payloadcms#13763)

* The `pagination` property was missing in `findVersions` and
`findGlobalVersions` Local API operations, although the actual functions
did have it -
https://github.com/payloadcms/payload/blob/1b93c4becc9f85684bc2d79795615aee101988fd/packages/payload/src/collections/operations/findVersions.ts#L25
* The handling of the `pagination` property in those functions was
broken, this PR fixes it.
…ms#13827)

Fixes an issue with the new experimental `enableListViewSelectAPI`
config option.

Group fields were not populating properly in the list view

### Before (incorrect)
```ts
{
  group.field: true
}
```

### After (correct)
```ts
{
  group: {
    field: true
  }
}
```
payloadcms#13828)

When using `pnpm next build --experimental-build-mode compile` in our
website template, Next.js outputs the following code:

<img width="2322" height="1262" alt="Screenshot 2025-09-16 at 14 39
31@2x"
src="https://github.com/user-attachments/assets/bb7dc5c1-ffe7-4ee7-8864-19cb2147c07d"
/>

If you then run `pnpm next build --experimental-build-mode generate` or
`pnpm next build --experimental-build-mode generate-env` which inlines
the env variable, it becomes the following code:

<img width="2276" height="1340" alt="Screenshot 2025-09-16 at 14 40
14@2x"
src="https://github.com/user-attachments/assets/6fc8cbd4-4df8-4d9b-a3e5-fc299e147537"
/>

Suddenly, it no longer assigns to a variable but to a string, which is
invalid JavaScript syntax, throwing the following error:

<img width="2314" height="320" alt="Screenshot 2025-09-16 at 14 40
53@2x"
src="https://github.com/user-attachments/assets/2c3487be-82e3-4043-aa04-0d98f0a05b4b"
/>

This PR works around this issue changing up the source code in
`getServerSideURL`.

I have reported this issue to Next.js:
vercel/next.js#83863


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211375615406662
…ayloadcms#13815)

Wrap `EmailField` and `TextField` with `FieldPathContext` to ensure the
inputs bind to their own paths (`"email"`, `"username"`) instead of
inheriting the relationship field name (e.g., `"author"`).
This restores correct `name` / `schemaPath` binding and fixes form
submission.

Regression introduced in payloadcms#11973.
Fixes payloadcms#13764.

This approach is consistent with the discussion in
[payloadcms#13806](payloadcms#13806), where it was
suggested that enforcing field paths explicitly is the right direction.

**What?**
Fixes regression where inline create drawer fields (`EmailField`,
`TextField`) incorrectly inherited the parent relationship field name,
breaking form submission.

**Why?**
Without this fix, the email input in inline create forms was bound to
the relationship field name (e.g., `"author"`) instead of `"email"`,
causing authentication-enabled collections to fail when creating users
inline.

**How?**

* Wrap `EmailField` with `FieldPathContext value="email"`
* Wrap `TextField` with `FieldPathContext value="username"`
* Ensures inputs register under the correct paths in form state.

Fixes payloadcms#13764.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

keep Prevents from being marked stale or auto-closed. v2