### What?
Fixes the issue when visiting the create view with the Join Field and
using postgres adapter
```
invalid input syntax for type integer: "NaN"
```
This happens because we don't have an ID yet and we send to the
database:
`WHERE id = NaN`
### How?
Avoids calling `getTableState` inside of `RelationshipTable` if there's
no ID yet, as it will always lead to the same empty result. While we
_could_ avoid error directly in the database adapter, I don't think we
should do that render request
Fixes https://github.com/payloadcms/payload/issues/9193
The field RSC now provides an initial state for all lexical blocks. This
completely obliterates any flashes and lexical block loading states when
loading or saving a document.
Previously, when a document is loaded or saved, every lexical block was
sending a network request in order to fetch their form state. Now, this
is batched and handled in the lexical server component. All lexical
block form states are sent to the client together with the parent
lexical field, and are thus available immediately.
We also do the same with block collapsed preferences. Thus, there are no
loading states or layout shifts/flashes of blocks anymore.
Additionally, when saving a document while your cursor is inside a
lexical field, the cursor position is preserved. Previously, a document
save would kick your cursor out of the lexical field.
## Look at how nice this is:
https://github.com/user-attachments/assets/21d736d4-8f80-4df0-a782-7509edd993da
**BREAKING:**
This removes the `feature.hooks.load` and `feature.hooks.save`
interfaces from custom lexical features, as they weren't used internally
and added unnecessary, additional overhead.
If you have custom features that use those, you can migrate to using
normal payload hooks that run on the server instead of the client.
With this PR, you can now customize the way that `blocks` and
`inlineBlocks` are rendered within Lexical's `BlocksFeature` by passing
your own React components.
This is super helpful when you need to create "previews" or more
accurate UI for your Lexical blocks.
For example, let's say you have a `gallery` block where your admins
select a bunch of images. By default, Lexical would just render a
collapsible with your block's fields in it. But now you can customize
the `admin.components.Block` property on your `block` config by passing
it a custom React component for us to render instead.
So using that, with this `gallery` example, you could make a dynamic
gallery React component that shows the images to your editors - and then
render our built-in `BlockEditButton` to allow your editors to manage
your gallery in a drawer.
Here is an example where the BlockEditButton is added to the default
Block Collapsible/Header:

---------
Co-authored-by: James <james@trbl.design>
Protects the `/api/access` endpoint behind authentication and sanitizes
the result, making it more secure and significantly smaller. To do this:
1. The `permission` keyword is completely omitted from the result
2. Only _truthy_ access results are returned
3. All nested permissions are consolidated when possible
---------
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
**BREAKING:**
Improves type-safety of collection / global slugs by using `CollectionSlug` / `UploadCollectionSlug` and `GlobalSlug` types instead of `string` in these places:
Adds `UploadCollectionSlug` and `TypedUploadCollection` utility types
This also changes how we suggest to add an upload collection to a cloud-storage adapter:
Before:
```ts
azureStorage({
collections: {
[Media.slug]: true,
},
})
```
After:
```ts
azureStorage({
collections: {
media: true,
},
})
```
The collection list columns are stored as user preferences to the
payload-preferences collection. Normally one user should never have
duplicate documents with the same key. This is controlled by using an
upsert normally. The collection list does not have a good way to call
upsert and was creating preferences documents every time. This change
makes it so that existing preferences are updated rather than created
with each column change.
### What?
Aligns types for HiddenField and the WatchCondition component with the
rest of the fields. Since path is required when rendering a Field
component, there is no need to keep it optional in the WatchCondition
component.
### Why?
Hidden fields were requiring the `field` property to be passed, but the
only reason it needed it was to allow the path to fallback to name if
path was not passed. But path is required so there is no need for this
anymore.
This makes using the HiddenField simpler now.
### How?
Adjusts type on the HiddenField and the WatchCondition component.
### What?
Changes the order of the `DefaultCellComponentProps` generic type,
allowing us to infer the type of cellData when a ClientField type is
passed as the first generic argument. You can override the cellData type
by passing the second generic.
Previously:
```ts
type DefaultCellComponentProps<TCellData = any, TField extends ClientField = ClientField>
```
New:
```ts
type DefaultCellComponentProps<TField extends ClientField = ClientField, TCellData = undefined>
```
### Why?
Changing the ClientField type to be the first argument allows us to
infer the cellData value type based on the type of field.
I could have kept the same signature but the usage would look like:
```ts
// Not very DX friendly
const MyCellComponent<DefaultCellComponentProps<,ClientField>> = () => null
```
### How?
The changes made
[here](https://github.com/payloadcms/payload/compare/chore/beta/simplify-DefaultCellComponentProps?expand=1#diff-24f3c92e546c2be3fed0bab305236bba83001309a7239c20a3e3dbd6f5f71dc6R29-R73)
allow this. You can override the type by passing in the second argument
to the generic.
### What?
Exposes DefaultServerCellComponentProps type for custom server cell
components.
### Why?
So users can type their custom server cell components properly.
### What?
List column state could become out of sync if toggling columns happened
in rapid succession as seen in CI. Or when using a spotty connection
where responses could come back out of order.
### Why?
State was not being preserved between toggles. Leading to incorrect
columns being toggled on/off.
### How?
Updates internal column state before making the request to the server so
when a future toggle occurs it has up to date state of all columns. Also
introduces an abort controller to prevent the out of order response
issue.
### What?
Ensures `path` is required and only present on the fields that expect it
(all fields except row).
Deprecates `useFieldComponents` and `FieldComponentsProvider` and
instead extends the RenderField component to account for all field
types. This also improves type safety within `RenderField`.
### Why?
`path` being optional just adds DX overhead and annoyance.
### How?
Added `FieldPaths` type which is added to iterable field types. Placed
`path` back onto the ClientFieldBase type.
This PR fixes and improves a few things around localisation and
fallbackLocale:
- For the REST API `fallbackLocale` and `fallback-locale` are treated
the same for consistency with the Local API
- `fallback: false` in config is now respected, by default results will
not fallback to `defaultLocale` unless this config is true, can also be
overridden by providing an explicit `fallbackLocale` in the request
- locale specific fallbacks will now take priority over `defaultLocale`
unless an explicit fallback is provided
- Fixes types on operations to allow `'none'` as a value for
fallbackLocale
- `fallback` is now true by default if unspecified
Closes https://github.com/payloadcms/payload/issues/8443
### What?
Adds `serverProps` and `clientProps` to custom list view slot
components.
### Why?
They were missing and should be exposed.
### How?
Created custom types for list slot components and threads them through
into `renderListSlots` function and passes them through to each
`RenderServerComponent` that renders list view slot components.
### What?
Removes abort controllers that were shared globally inside the server
actions provider.
### Why?
Constructing them in this way will cause different fetches using the
same function to cancel one another accidentally.
These are currently causing issues when two components call server
functions, even different functions, because the global ref inside was
being overwritten and aborting the previous one.
### How?
Standardizes how we construct and destroy abort controllers. This PR is focused around creating them to pass into the exposed serverAction provider functions. There are other places where this pattern can be applied.
This fixes a peer dependency error in our monorepo, as
eslint-plugin-jsx-a11y finally supports eslint v9.
Additionally, this officially adds TypeScript 5.6 support for
typescript-eslint.
1. Open fields test suite
2. Type in relationship field, that has a relation to the numbers
collection
3. Scroll
You will get an error, as the label for the entry corresponding to the
numbers collection is of type number, and it attempts to use the
.toString() method on it
Currently, Payload renders all custom components on initial compile of
the admin panel. This is problematic for two key reasons:
1. Custom components do not receive contextual data, i.e. fields do not
receive their field data, edit views do not receive their document data,
etc.
2. Components are unnecessarily rendered before they are used
This was initially required to support React Server Components within
the Payload Admin Panel for two key reasons:
1. Fields can be dynamically rendered within arrays, blocks, etc.
2. Documents can be recursively rendered within a "drawer" UI, i.e.
relationship fields
3. Payload supports server/client component composition
In order to achieve this, components need to be rendered on the server
and passed as "slots" to the client. Currently, the pattern for this is
to render custom server components in the "client config". Then when a
view or field is needed to be rendered, we first check the client config
for a "pre-rendered" component, otherwise render our client-side
fallback component.
But for the reasons listed above, this pattern doesn't exactly make
custom server components very useful within the Payload Admin Panel,
which is where this PR comes in. Now, instead of pre-rendering all
components on initial compile, we're able to render custom components
_on demand_, only as they are needed.
To achieve this, we've established [this
pattern](https://github.com/payloadcms/payload/pull/8481) of React
Server Functions in the Payload Admin Panel. With Server Functions, we
can iterate the Payload Config and return JSX through React's
`text/x-component` content-type. This means we're able to pass
contextual props to custom components, such as data for fields and
views.
## Breaking Changes
1. Add the following to your root layout file, typically located at
`(app)/(payload)/layout.tsx`:
```diff
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
+ import type { ServerFunctionClient } from 'payload'
import config from '@payload-config'
import { RootLayout } from '@payloadcms/next/layouts'
import { handleServerFunctions } from '@payloadcms/next/utilities'
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {
children: React.ReactNode
}
+ const serverFunctions: ServerFunctionClient = async function (args) {
+ 'use server'
+ return handleServerFunctions({
+ ...args,
+ config,
+ importMap,
+ })
+ }
const Layout = ({ children }: Args) => (
<RootLayout
config={config}
importMap={importMap}
+ serverFunctions={serverFunctions}
>
{children}
</RootLayout>
)
export default Layout
```
2. If you were previously posting to the `/api/form-state` endpoint, it
no longer exists. Instead, you'll need to invoke the `form-state` Server
Function, which can be done through the _new_ `getFormState` utility:
```diff
- import { getFormState } from '@payloadcms/ui'
- const { state } = await getFormState({
- apiRoute: '',
- body: {
- // ...
- },
- serverURL: ''
- })
+ const { getFormState } = useServerFunctions()
+
+ const { state } = await getFormState({
+ // ...
+ })
```
## Breaking Changes
```diff
- useFieldProps()
- useCellProps()
```
More details coming soon.
---------
Co-authored-by: Alessio Gravili <alessio@gravili.de>
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
Co-authored-by: James <james@trbl.design>
Closes#9000
When you update a relationship document via the document drawer, the
initial document is registering `modified: true`. We should only set
modified to true on the initial document if the relationship id has
changed.
### What?
Uses sequential pattern for Bulk Upload instead of `Promise.all`.
### Why?
* Concurrent uploads led to filename conflicts for example when you have
`upload.png` and `upload(1).png` already and you try to upload
`upload.png`
* Potentially expensive for resources, especially with high amount of
files / sizes
### How?
Replaces `Promise.all` with `for` loop, adds indicator "Uploaded 2/20"
to the loading overlay.
---------
Co-authored-by: James <james@trbl.design>
Setting a custom `id` field within unnamed fields causes duplicative ID
fields to be appear in the client config. When a top-level `id` field is
detected in your config, Payload uses that instead of injecting its
default field. But when nested within unnamed fields, such as an unnamed
tab, these custom `id` fields were not being found, causing the default
field to be duplicately rendered into tables columns, etc.