### What?
Currently it is not possible to override the upload component.
### Why?
The ability to override the upload component is missing from
`renderDocumentSlots`.
### How?
Adding a check to include the custom upload component if it is
available.
This issue is holding me back from releasing a payload plugin.
Fixes#9591
Previously, we were unnecessarily passing the `ClientCollectionConfig`
down from the Table to the Client, even though the client cell
components could simply access it via the `useConfig` hook. This
resulted in redundant data being sent to the client for every single
table cell. Additionally, we were performing a deep copy of the
`ClientCollectionConfig`, which wasted both memory and CPU resources on
the server.
---------
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Custom block row labels defined on `admin.components.Label` were not
rendering despite existing in the config. Instead, if a custom label
component was defined on the _top-level_ blocks field itself, it was
incorrectly replacing each blocks label _in addition_ to the field's
label. Now, custom labels defined at the field-level now only replace
the field's label as expected, and custom labels defined at the
block-level are now supported as the types suggest.
Previously, the beforeValidate hook was deepCopying input data. This
indirectly ensured that the passed input data was not mutated.
E.g., if you run the `payload.create` local API, you do not want that to
mutate the `data` object that is passed as an argument. This will create
issues if you attempt to use it multiple times.
This PR moves the deepCopy logic from the beforeValidate hook to the
start of the local API operation. This ensures that
- Input data is intentionally copied at the beginning which makes more
sense. Comment was added to explain why
- GraphQL and Rest operations are now faster, as we don't need to ensure
that the input data is not mutated for those => deepCopy only runs for
local API
Following https://github.com/payloadcms/payload/pull/10551, I found and
fixed a handful more bugs:
- When writing to the input, the results that were already there were
not cleaned, causing incorrect results to appear.
- the scroll was causing an infinite loop that showed repeated elements
- optimization: only the required field is selected (not required)
- refs are set to the initial value to avoid a state where nothing can
be searched
Having the `scripts` dir re-use all packages from the top-level was
getting quite unwieldy. Created new `tools` directory that is part of
the workspace. Packages are exported with the `@tools` package
namespace.
https://github.com/payloadcms/payload/pull/9962 could be considered a
breaking change - this PR restores compatibility by allowing unknown
collection slugs, while still providing type suggestions.
The custom components example no longer ran seed on init. This is done
through a preconfigured migration script that automatically runs on
startup. The `@payloadcms/graphql` package was also incorrectly
installed as a dev dependency and the lockfile was significantly out of
date. The `react` and `react-dom` packages were also pinned to v19.0.0,
with their corresponding types packages to v19.0.1. These now all match
as expected and are identified using the caret operator to ensure the
latest versions are installed.
Bumps the following dependencies:
- next
- typescript
- http-status
- nodemailer
- Payload & next versions in all templates
- Monorepo only: playwright and dotenv
Removes unused dependencies:
- ts-jest
- jest-environment-jsdom
- resend (we don't use their sdk, we only use their rest API)
Previously images would not be revalidated if they were cropped or
changed in another ways.
Now if the image is updated, they will also be updated on the frontend
whenever a post is revalidated.
There was a rogue `{/* IMAGE OF LIVE PREVIEW HERE */}` comment being
rendered in the live preview docs. Comments in this format used to be
hidden, but since moving to a new rendering pattern they now appear.
### What?
Previously, field error messages displayed in toast notifications used
the field path to reference fields that failed validation. This
path-based approach was necessary to distinguish between fields that
might share the same name when nested inside arrays, groups, rows, or
collapsible fields.
However, the human readability of these paths was lacking, especially
for unnamed fields like rows and collapsible fields. For example:
- A text field inside a row could display as: `_index-0.text`
- A text field nested within multiple arrays could display as:
`items.0.subArray.0.text`
These outputs are technically correct but not user-friendly.
### Why?
While the previous format was helpful for pinpointing the specific field
that caused the validation error, it could be more user-friendly and
clearer to read. The goal is to maintain the same level of accuracy
while improving the readability for both developers and content editors.
### How?
To improve readability, the following changes were made:
1. Use Field Labels Instead of Field Paths:
- The ValidationError component now uses the label prop from the field
config (if available) instead of the field’s name.
- If a label is provided, it will be used in the error message.
- If no label exists, it will fall back to the field’s name.
2. Remove _index from Paths for Unnamed Fields (In the validationError
component only):
- For unnamed fields like rows and collapsibles, the _index prefix is
now stripped from the output to make it cleaner.
- Instead of `_index-0.text`, it now outputs just `Text`.
3. Reformat the Error Path for Readability:
- The error message format has been improved to be more human-readable,
showing the field hierarchy in a structured way with array indices
converted to 1-based numbers.
#### Example transformation:
##### Before:
The following fields are invalid: `items.0.subArray.0.text`
##### After:
The following fields are invalid: `Items 1 > SubArray 1 > Text`
### What?
The `pasteURL` feature for Upload fields has been updated to support
both **client-side** and **server-side** URL fetching. Previously, users
could only paste URLs from the same domain as their Payload instance
(internal) or public domains, which led to **CORS** errors when trying
to fetch files from external URLs.
Now, users can choose between **client-side fetching** (default) and
**server-side fetching** using the new `pasteURL` option in the Upload
collection config.
### How?
- By default, Payload will attempt to fetch the file client-side
directly in the browser.
- To enable server-side fetching, you can configure the new `pasteURL`
option with an `allowList` of trusted domains.
- The new `/api/:collectionSlug/paste-url` endpoint is used to fetch
files server-side and stream them back to the browser.
#### Example
```
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
upload: {
// pasteURL: false, // Can now disable the pasteURL option entirely by passing "false".
pasteURL: {
allowList: [
{
hostname: 'payloadcms.com', // required
pathname: '',
port: '',
protocol: 'https', // defaults to https - options: "https" | "http"
search: ''
},
{
hostname: 'example.com',
pathname: '/images/*',
},
],
},
},
}
```
### Why
This update provides more flexibility for users to paste URLs into
Upload fields without running into **CORS errors** and allows Payload to
securely fetch files from trusted domains.
This adds support for calculating and displaying file sizes for JPEG XL
images.
Image resizing is not supported by sharp out-of-the-box yet:
https://github.com/lovell/sharp/issues/2731
### What?
Extends visibility into what view is being shown so custom components
have context as to where they are being rendered.
**This PR does not add React Context.**
### Why?
This was needed for the multi-tenant plugin where the selector is in the
navigation sidebar and has no way to know if it is being shown inside of
a document or the list view.
I assume other users may also want their server components to be aware
of where a component is rendering before hitting the client. An example
would be wanting to redirect on the server instead of on the client,
this is how multi-tenant redirects users from "global" enabled
collections to the document view.
### How?
Adds 2 new variables that are determined by the view being routed to.
`viewType` - which view is being rendered, ie `list`, `document`,
`version`, `account`, `verify`, `reset`
```ts
type ViewTypes =
| 'account'
| 'dashboard'
| 'document'
| 'list'
| 'reset'
| 'verify'
| 'version'
```
`documentSubViewType` - which tells you what sub view you are on, ie
`api`, `livePreview`, `default`, `versions`
```ts
type DocumentSubViewTypes =
| 'api'
| 'default'
| 'livePreview'
| 'version'
| 'versions'
```
- reduces unnecessary shallow copying within operations by removing
unnecessary spreads or .map()'s
- removes unnecessary `deleteMany` call in `deleteUserPreferences` for
auth-enabled collections
- replaces all instances of `validOperators.includes` with
`validOperatorMap[]`. O(n) => O(1)
- optimizes the `sanitizeInternalFields` function. Previously, it was
doing a **lot** of shallow copying
A lot of this deepCopying was just not necessary. This removes the
deepCopying from all field hook operations where I think it's 100% safe.
It does not remove all deepCopying, especially in areas where the input
data was deep copied, and that data pre-modification is then used after
the field hooks have run.
In these cases, further execution of the hook might be intentionally
expecting the unmodified version of that input data
Data for the EditMany view was fetched even though the EditMany Drawer
was not open. This, in combination with the router.replace call to add
the default limit query param, caused the root layout to re-render
### What?
General improvements:
- Disable duplication on tenant collections marked with `isGlobal`
- Simplify cookie setting logic and option loading for the selector