When adding a custom ID field to an array's config, both the default
field provided by Payload, and the custom ID field, exist in the
resulting config. This can lead to problems when the looking up the
field's config, where either one or the other will be returned.
Fixes#12978
Adds `restrictedFileTypes` (default: `false`) to upload collections
which prevents files on a restricted list from being uploaded.
To skip this check:
- set `[Collection].upload.restrictedFileTypes` to `true`
- set `[Collection].upload.mimeType` to any type(s)
This adds a new `analyze` step to our CI that analyzes the bundle size
for our `payload`, `@payloadcms/ui`, `@payloadcms/next` and
`@payloadcms/richtext-lexical` packages.
It does so using a new `build:bundle-for-analysis` script that packages
can add if the normal build step does not output an esbuild-bundled
version suitable for analyzing. For example, `ui` already runs esbuild,
but we run it again using `build:bundle-for-analysis` because we do not
want to split the bundle.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1210692087147570
### What?
Adds `token` into the `initialState` for the reset password form to
ensure `token` does not get passed through as `undefined`.
### Why?
Currently the reset password UI is broken because `token` is not getting
passed to the form state.
### How?
Adds `token` to `initialState`
Fixes#13040
Fixes https://github.com/payloadcms/payload/issues/13045
`updateOne` when returning is `false` mutates the data object for write
operations on the DB, but that causes an issue when using that data
object later on since all of the id's are mutated to objectIDs and never
transformed back into read id's.
This fix ensures that the transform happens even when the result is not
returned.
As stated in #12529, the setTimeout was defined through trial and error
as it wasn't possible to reproduce the bug with the devtools open and
therefore with the CPU throttled. One user reported still experiencing
the bug.
I'm increasing the timeout to 100ms, which seems acceptable enough to
keep postponing a better fix, considering the bug isn't that critical.
If we find it keeps happening, we'll probably need to investigate the
root cause.
### What?
This PR updates the `from` field in `plugin-redirects` to add `unique:
true`.
### Why?
If you create multiple redirects with the same `from` URL — the
application won't know which one to follow, which causes errors and
unpredictable behavior.
### How?
Adds `unique: true` to the plugin injected `from` field.
### Migration Required
This change will require a migration. Projects already using this plugin
will need to:
- Ensure there are no duplicate `from` values in their existing
redirects collection.
- Remove or modify any duplicate entries before applying this update.
Fixes#12959
Adds:
```ts
import { lookup } from 'dns/promises'
// ...
const { address } = await lookup(hostname)
// ...
return isSafeIp(address)
```
To ensure that an `ip` address is being verified. Previously, hostnames
were being verified by `isSafeIp`.
Fixes: https://github.com/payloadcms/payload/issues/12876
### What?
Fixes an issue where only the fields from the first batch of documents
were used to generate CSV column headers during streaming exports.
### Why?
Previously, columns were determined during the first streaming batch. If
a field appeared only in later documents, it was omitted from the CSV
entirely — leading to incomplete exports when fields were sparsely
populated across the dataset.
### How?
- Adds a **pre-scan step** before streaming begins to collect all column
keys across all pages
- Uses this superset of keys to define the final CSV header
- Ensures every row is padded to match the full column set
This matches the behavior of non-streamed exports and guarantees that
the streamed CSV output includes all relevant fields, regardless of when
they appear in pagination.
This fixes the payload bundle script. While not run by default, it's
useful for checking the payload bundle size by manually running `cd
packages/payload && node bundle.js`.
Previously, `db.updateOne` calls with `where` queries that lead to no
results would create new rows on drizzle. Essentially, `db.updateOne`
behaved like `db.upsertOne` on drizzle
Removing the `setTimeout` not only doesn't break any tests, but it also
fixes the linked issue.
The long comment above the if statement was added in
https://github.com/payloadcms/payload/pull/5460 and explains why the if
statement is necessary GIVEN the existence of the `setTimeout`, but the
`setTimeout` was introduced [earlier because the button apparently
didn't work](https://github.com/payloadcms/payload/issues/1414).
It seems to work now without the `setTimeout`, because otherwise the
tests wouldn't even pass. I also tested it manually, and it works fine.
Fixes#12687
### What?
Shows error state (red left border) on small screens.
### Why?
The current error state disappears at small-break screen width.
### How?
Updates small-break error state to match the desktop error state for the
Lexical field.
##### Reported by client.
Required for #13005.
Opening an autosave-enabled document within a drawer triggers an
infinite loop when the root document is also autosave-enabled.
This was for two reasons:
1. Autosave would run and change the `updatedAt` timestamp. This would
trigger another run of autosave, and so on. The timestamp is now removed
before comparison to ensure that sequential autosave runs are skipped.
2. The `dequal()` call was not being given the `.current` property off
the ref object. This meant that is was never evaluate to `true` and
therefore never skip unnecessary autosaves to begin with.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1210697235723932
### What?
Ensure the export preview table includes all field keys as columns, even
if those fields are not populated in any of the returned documents.
### Why?
Previously, if none of the documents in the preview result had a value
for a given field, that column would be missing entirely from the
preview table.
### How?
- Introduced a `getFlattenedFieldKeys` utility that recursively extracts
all missing flattened field accessors from the collection’s config that
are undefined
- Updates the preview UI logic to build columns from all flattened keys,
not just the first document
Adds support for `halfvec` and `sparsevec` and `bit` (binary vector)
column types. This is required for supporting indexing of embeddings >
2000 dimensions on postgres using the pg-vector extension.
### What?
This PR updates the `create` access control on the `users` collection in
the `multi-tenant` example to prevent unauthorized creation of
`super-admin` users.
### Why?
Previously, any authenticated user could create a new user and assign
them the `super-admin` role — even if they didn’t have that role
themselves. This bypassed role-based restrictions and introduced a
security vulnerability, allowing users to escalate their own privileges
by working around role restrictions during user creation.
### How?
The `create` access function now checks whether the current user has the
`super-admin` role before allowing the creation of another
`super-admin`. If not, the request is denied.
**Fixes:** `CMS2-Q225-01`
### What
This PR updates the `create` access control functions in the
`multi-tenant` example to ensure that any `tenant` specified in a create
request matches a tenant the user has admin access to.
### Why
Previously, while the admin panel UI restricted the tenant selection, it
was still possible to bypass this by making a request directly to the
API with a different `tenant`. This allowed users to create documents
under tenants they shouldn't have access to.
### How
The `access` functions on the `users` and `pages` collections now
explicitly check whether the tenant(s) in the request are included in
the user's tenant permissions. If not, access is denied by returning
`false`.
**Fixes: CMS2-Q225-03**
Supersedes #12992. Partially closes#12975.
Right now autosave-enabled documents opened within a drawer will
unnecessarily remount on every autosave interval, causing loss of input
focus, etc. This makes it nearly impossible to edit these documents,
especially if the interval is very short.
But the same is true for non-autosave documents when "manually" saving,
e.g. pressing the "save draft" or "publish changes" buttons. This has
gone largely unnoticed, however, as the user has already lost focus of
the form to interact with these controls, and they somewhat expect this
behavior or at least accept it.
Now, the form remains mounted across autosave events and the user's
cursor never loses focus. Much better.
Before:
https://github.com/user-attachments/assets/a159cdc0-21e8-45f6-a14d-6256e53bc3df
After:
https://github.com/user-attachments/assets/cd697439-1cd3-4033-8330-a5642f7810e8
Related: #12842
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1210689077645986
### What?
Fixes a bug where adding an additional OR filter condition in the list
view selects a field with `admin.disableListFilter: true`, causing all
filter fields to appear disabled.
### Why?
When the first field in a collection has `disableListFilter` set to
`true`, adding a second OR condition defaults to using that field. This
leads to a broken filter UI where no valid fields are selectable.
### How?
Replaces the hardcoded usage of `reducedFields[0]` with a call to
`reducedFields.find(...) `that skips fields with `disableListFilter:
true`, consistent with the logic already used when adding the first
filter condition.
Fixes#12993
### What?
The "Preview Sizes" button in the file upload UI was not showing up if:
- `crop` and `focalPoint` were both `false`
- No `customUploadActions` were provided
- But image sizes were configured
### Why?
This happened because `UploadActions` wasn’t rendered at all unless
adjustments or custom actions were present.
### How?
Update the conditional in `StaticFileDetails` to also render
`UploadActions` when:
- `hasImageSizes` is `true` and the document has a `filename`
Fixes#12832
When using pnpm 10 to install any of our templates, the following
warning is thrown:

> Warning: Ignored build scripts: esbuild, unrs-resolver. Run "pnpm
approve-builds" to pick which dependencies should be allowed to run
scripts.
This PR fixes this by adding those packages to `onlyBuiltDependencies`
This PR consists of two separate changes. One change cannot pass CI
without the other, so both are included in this single PR.
## CI - ensure types are generated
Our website template is currently failing to build due to a type error.
This error was introduced by a change in our generated types.
Our CI did not catch this issue because it wasn't generating types /
import map before attempting to build the templates. This PR updates the
CI to generate types first.
It also updates some CI step names for improved clarity.
## Fix: type error

This fixes the type error by ensuring we consistently use the _same_
generated `TypedUser` object within payload, instead of `BaseUser`.
Previously, we sometimes used the generated-types user and sometimes the
base user, which was causing type conflicts depending on what the
generated user type was.
It also deprecates the `User` type (which was essentially just
`BaseUser`), as consumers should use `TypedUser` instead. `TypedUser`
will automatically fall back to `BaseUser` if no generated types exists,
but will accept passing it a generated-types User.
Without this change, additional properties added to the user via
generated-types may cause the user object to not be accepted by
functions that only accept a `User` instead of a `TypedUser`, which is
what failed here.
## Templates: re-generate templates to update generated types
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1210668927737258
Fixes#12826
Leave without saving was being triggered when no changes were made to
the tenant. This should only happen if the value in form state differs
from that of the selected tenant, i.e. after changing tenants.
Adds tenant selector syncing so the selector updates when a tenant is
added or the name is edited.
Also adds E2E for most multi-tenant admin functionality.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1210562742356842
<!--
Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.
Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.
The following items will ensure that your PR is handled as smoothly as
possible:
- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change
### What?
### Why?
### How?
Fixes #
-->
### What?
This PR addresses an issue where the order of operations/conditions for
throwing an unverified email error were incorrect.
### Why?
To properly throw an unverified email error under the correct
conditions.
### How?
Pushing this error to be thrown later in the operation.
Mounts live preview to `../:id` instead `../:id/preview`.
This is a huge win for both UX and a maintainability standpoint.
Here are just a few of those wins:
1. If you edit a document, _then_ decide you want to preview those
changes, you are currently presented with the `LeaveWithoutSaving` modal
and are forced to either save your edits or clear them. This is because
you are being navigated to an entirely new page with it's own form
context. Instead, you should be able to freely navigate back and forth
between the two.
2. If you are an editor who most often uses Live Preview, or you are
editing a collection that typically requires it, you likely want it to
automatically enter live preview mode when you open a document.
Currently, the user has to navigate to the document _first_, then use
the live preview tab. Instead, you should be able to set a preference
and avoid this extra step.
3. Since the inception of Live Preview, we've been maintaining largely
the same code across the default edit view and the live preview view,
which often became out of sync and inconsistent—but they're essentially
doing the same thing. While we could abstract a lot of this out, it is
no longer necessary if the two views are combined into one.
This change does also include some small modifications to UI. The "Live
Preview" tab no longer exists, and instead has been replaced with a
button placed next to the document controls (subject to change).
Before:
https://github.com/user-attachments/assets/48518b02-87ba-4750-ba7b-b21b5c75240a
After:
https://github.com/user-attachments/assets/a8ec8657-a6d6-4ee1-b9a7-3c1173bcfa96
### What?
Adds optional chaining when accessing `rows` in `mergeServerFormState`
to prevent error crashing the UI.
### Why?
When an array field is populated in a `beforeChange` hook and was
previously empty, it crashes `mergeServerFormState.ts` on this line
because no `rows` exist:
```ts
const indexInCurrentState = currentState[path].rows.findIndex
```
The line after this checks `if (indexInCurrentState > -1)` so returning
undefined here will not affect the subsequent code.
### How?
Added optional chaining to the access of `rows`, which prevents the
error being thrown.
Fixes#12944
Adds full session functionality into Payload's existing local
authentication strategy.
It's enabled by default, because this is a more secure pattern that we
should enforce. However, we have provided an opt-out pattern for those
that want to stick to stateless JWT authentication by passing
`collectionConfig.auth.useSessions: false`.
Todo:
- [x] @jessrynkar to update the Next.js server functions for refresh and
logout to support these new features
- [x] @jessrynkar resolve build errors
---------
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
Co-authored-by: Jessica Chowdhury <jessica@trbl.design>
Co-authored-by: Jarrod Flesch <30633324+JarrodMFlesch@users.noreply.github.com>
Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
Needed for #12860.
The new live preview pattern requires collection-level preferences, a
pattern that does not yet exist.
Instead of creating a new record for these types of preferences, we can
simply reuse `<collectionSlug>-list` under a more general key:
`collection-<slug>`. This way other relevant properties can be attached
in the future that might not specifically apply to the list view.
This will also match the conventions already estalished by
document-level preferences in `collection-<slug>-<id>` and
`global-<slug>`.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1210628212784050
This pull request updates the `Card` component in the localization
example to support localized URLs. The most significant changes include
importing a new hook for locale management and modifying the URL
generation logic to include the locale.
Localization updates:
*
[`examples/localization/src/components/Card/index.tsx`](diffhunk://#diff-619212c47638e7ff51284c62740ba188c87f008d481442b7f4951e2c150a2415R5):
Imported `useLocale` from `next-intl` to manage locale-based
functionality.
*
[`examples/localization/src/components/Card/index.tsx`](diffhunk://#diff-619212c47638e7ff51284c62740ba188c87f008d481442b7f4951e2c150a2415R20):
Added a `locale` constant using the `useLocale` hook to retrieve the
current locale.
*
[`examples/localization/src/components/Card/index.tsx`](diffhunk://#diff-619212c47638e7ff51284c62740ba188c87f008d481442b7f4951e2c150a2415L28-R30):
Updated the `href` generation logic to include the locale in the URL
structure, ensuring localized navigation.
When using 3rd party custom components in an edit form there exists a
possibility that a non-navigational click event will propagate through
to payload.
In this case the `findClosestAnchor` function in `usePreventLeave` may
find an anchor without href, resulting in the `newUrlObj = new
URL(newUrl)` in `isAnchorOfCurrentUrl` throwing the exception:
> TypeError: URL constructor: is not a valid URL.
As a result a native alert is shown to the user, with no real
explanation as to what is going on. This is not a good experience.
I suggest moving it to a console log which is less "in your face" for
users who do not know what to do about it anyway.
I discovered this while using a data grid component with a context menu.
Clicking on menu items (which are `<a>` tags without href in this
component) triggers the error.
(Another on-liner fix would ofc be to not attempt to create an URL
object if there is no href `if (anchor?.href) {`, but I opted for this
version since using `alert()` in production code is not a preferred
practice anyway)
<!--
Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.
Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.
The following items will ensure that your PR is handled as smoothly as
possible:
- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change
### What?
### Why?
### How?
Fixes #
-->