### What?
Updates the join field documentation.
Mentions:
* Now you can specify an array of `collection` -
https://github.com/payloadcms/payload/pull/10919
* Querying limitation for join fields, planned
https://github.com/payloadcms/payload/discussions/9683
* Querying limitation for joined documents when the join field has an
array of `collection` for fields inside arrays and blocks.
### Why?
To have up to date documentation for an array of `collection` and so
users can know about limitations.
### How?
Updates the file on path `docs/fields/join.mdx`.
Adds a dedicated "Custom Components" section to the docs.
As users become familiar with building custom components, not all areas
that support customization are well documented. Not only this, but the
current pattern does not allow for deep elaboration on these concepts
without their pages growing to an unmanageable size. Custom components
in general is a large enough topic to merit a standalone section with
subpages. This change will make navigation much more intuitive, help
keep page size down, and provide room to document every single available
custom component with snippets to show exactly how they are typed, etc.
This is a substantial change to the docs, here is the overview:
- The "Admin > Customizing Components" doc is now located at "Custom
Components > overview"
- The "Admin > Views" doc is now located at "Custom Components > Custom
Views"
- There is a new "Custom Components > Edit View" doc
- There is a new "Custom Components > List View" doc
- The information about root components within the "Admin > Customizing
Components" doc has been moved to a new "Custom Components > Root
Components" doc
- The information about custom providers within the "Admin > Customizing
Components" doc has been moved to a new "Custom Components > Custom
Providers" doc
Similar to the goals of #10743, #10742, and #10741.
Fixes#10872 and initial scaffolding for #10353.
Dependent on #11126.
This change will require the following redirects to be set up:
- `/docs/admin/hooks` → `/docs/admin/react-hooks`
- `/docs/admin/components` → `/docs/custom-components/overview`
- `/docs/admin/views` → `/docs/custom-components/views`
If you have multiple blocks that are used in multiple places, this can quickly blow up the size of your Payload Config. This will incur a performance hit, as more data is
1. sent to the client (=> bloated `ClientConfig` and large initial html) and
2. processed on the server (permissions are calculated every single time you navigate to a page - this iterates through all blocks you have defined, even if they're duplicative)
This can be optimized by defining your block **once** in your Payload Config, and just referencing the block slug whenever it's used, instead of passing the entire block config. To do this, the block can be defined in the `blocks` array of the Payload Config. The slug can then be passed to the `blockReferences` array in the Blocks Field - the `blocks` array has to be empty for compatibility reasons.
```ts
import { buildConfig } from 'payload'
import { lexicalEditor, BlocksFeature } from '@payloadcms/richtext-lexical'
// Payload Config
const config = buildConfig({
// Define the block once
blocks: [
{
slug: 'TextBlock',
fields: [
{
name: 'text',
type: 'text',
},
],
},
],
collections: [
{
slug: 'collection1',
fields: [
{
name: 'content',
type: 'blocks',
// Reference the block by slug
blockReferences: ['TextBlock'],
blocks: [], // Required to be empty, for compatibility reasons
},
],
},
{
slug: 'collection2',
fields: [
{
name: 'editor',
type: 'richText',
editor: lexicalEditor({
BlocksFeature({
// Same reference can be reused anywhere, even in the lexical editor, without incurred performance hit
blocks: ['TextBlock'],
})
})
},
],
},
],
})
```
## v4.0 Plans
In 4.0, we will remove the `blockReferences` property, and allow string block references to be passed directly to the blocks `property`. Essentially, we'd remove the `blocks` property and rename `blockReferences` to `blocks`.
The reason we opted to a new property in this PR is to avoid breaking changes. Allowing strings to be passed to the `blocks` property will prevent plugins that iterate through fields / blocks from compiling.
## PR Changes
- Testing: This PR introduces a plugin that automatically converts blocks to block references. This is done in the fields__blocks test suite, to run our existing test suite using block references.
- Block References support: Most changes are similar. Everywhere we iterate through blocks, we have to now do the following:
1. Check if `field.blockReferences` is provided. If so, only iterate through that.
2. Check if the block is an object (= actual block), or string
3. If it's a string, pull the actual block from the Payload Config or from `payload.blocks`.
The exception is config sanitization and block type generations. This PR optimizes them so that each block is only handled once, instead of every time the block is referenced.
## Benchmarks
60 Block fields, each block field having the same 600 Blocks.
### Before:
**Initial HTML:** 195 kB
**Generated types:** takes 11 minutes, 461,209 lines
https://github.com/user-attachments/assets/11d49a4e-5414-4579-8050-e6346e552f56
### After:
**Initial HTML:** 73.6 kB
**Generated types:** takes 2 seconds, 35,810 lines
https://github.com/user-attachments/assets/3eab1a99-6c29-489d-add5-698df67780a3
### After Permissions Optimization (follow-up PR)
Initial HTML: 73.6 kB
https://github.com/user-attachments/assets/a909202e-45a8-4bf6-9a38-8c85813f1312
## Future Plans
1. This PR does not yet deduplicate block references during permissions calculation. We'll optimize that in a separate PR, as this one is already large enough
2. The same optimization can be done to deduplicate fields. One common use-case would be link field groups that may be referenced in multiple entities, outside of blocks. We might explore adding a new `fieldReferences` property, that allows you to reference those same `config.blocks`.
Fixes#10440. When `filterOptions` are set on a relationship field,
those same filters are not applied to the `Filter` component within the
list view. This is because `filterOptions` is not being thread into the
`RelationshipFilter` component responsible for populating the available
options.
To do this, we first need to be resolve the filter options on the server
as they accept functions. Once resolved, they can be prop-drilled into
the proper component and appended onto the client-side "where" query.
Reliant on #11080.
Adds support for timezone selection on date fields.
### Summary
New `admin.timezones` config:
```ts
{
// ...
admin: {
// ...
timezones: {
supportedTimezones: ({ defaultTimezones }) => [
...defaultTimezones,
{ label: '(GMT-6) Monterrey, Nuevo Leon', value: 'America/Monterrey' },
],
defaultTimezone: 'America/Monterrey',
},
}
}
```
New `timezone` property on date fields:
```ts
{
type: 'date',
name: 'date',
timezone: true,
}
```
### Configuration
All date fields now accept `timezone: true` to enable this feature,
which will inject a new field into the configuration using the date
field's name to construct the name for the timezone column. So
`publishingDate` will have `publishingDate_tz` as an accompanying
column. This new field is inserted during config sanitisation.
Dates continue to be stored in UTC, this will help maintain dates
without needing a migration and it makes it easier for data to be
manipulated as needed. Mongodb also has a restriction around storing
dates only as UTC.
All timezones are stored by their IANA names so it's compatible with
browser APIs. There is a newly generated type for `SupportedTimezones`
which is reused across fields.
We handle timezone calculations via a new package `@date-fns/tz` which
we will be using in the future for handling timezone aware scheduled
publishing/unpublishing and more.
### UI
Dark mode

Light mode

This PR moves the logic for rendering diff field components in the
version comparison view from the client to the server.
This allows us to expose more customization options to the server-side
Payload Config. For example, users can now pass their own diff
components for fields - even including RSCs.
This PR also cleans up the version view types
Implements the following from
https://github.com/payloadcms/payload/discussions/4197:
- allow for customization of diff components
- more control over versions screens in general
TODO:
- [x] Bring getFieldPaths fixes into core
- [x] Cleanup and test with scrutiny. Ensure all field types display
their diffs correctly
- [x] Review public API for overriding field types, add docs
- [x] Add e2e test for new public API
### What?
This PR fixes many links in the docs as well as a few formatting and
grammar issues.
### Why?
To properly link users to the correct destination in the docs and
present well-formatted docs.
### How?
Changes to a few files in `docs/`
Field validations currently run very often, such as within form state on
type. This can lead to serious performance implications within the admin
panel if those validation functions are async, especially if they
perform expensive database queries. One glaring example of this is how
all relationship and upload fields perform a database lookup in order to
evaluate that the given value(s) satisfy the defined filter options. If
the field is polymorphic, this can happen multiple times over, one for
each collection. Similarly, custom validation functions might also
perform expensive tasks, something that Payload has no control over.
The fix here is two-fold. First, we now provide a new `event` arg to all
`validate` functions that allow you to opt-in to performing expensive
operations _only when documents are submitted_, and fallback to
significantly more performant validations as form state is generated.
This new pattern will be the new default for relationship and upload
fields, however, any custom validation functions will need to be
implemented in this way in order to take advantage of it. Here's what
that might look like:
```
[
// ...
{
name: 'text'
type: 'text',
validate: async (value, { event }) => {
if (event === 'onChange') {
// Do something highly performant here
return true
}
// Do something more expensive here
return true
}
}
]
```
The second part of this is to only run validations _after the form as
been submitted_, and then every change event thereafter. This work is
being done in #10580.
Similar to #10742. Collection and global-level admin options are
currently documented within the "admin > collections" and "admin >
globals" pages, respectively. This makes them hard to find because
users, myself included, intuitively navigate to the collection and
global overview docs to locate this information before realizing it
lives elsewhere. Now, they are rendered within "configuration >
collections" and "configuration > globals" as expected and the old pages
have been removed altogether.
Continuation of #10741. Field-level admin options, including the
conditional logic and custom field components, are currently documented
within the "admin > customizing views" page. This makes them hard to
find because users, myself included, intuitively navigate to the fields
overview doc first to locate this information. Now, they are rendered
within "fields > overview" as expected. This should help keep the user
from jumping around from doc to doc and getting lost.
Although the "customizing fields" doc provides a big picture overview of
how to create custom field components, it is not explicit enough for
developers to know exactly where to start. For example, it can be
challenging to import the correct types when building these components,
and the natural place to go looking for this information is on the
fields docs themselves. Now, each field doc has its own dedicated
"custom components" section which provides concrete examples for fields
and field labels in both server and client component format, with more
examples to come over time such as using inputs directly, etc. In the
same vein, the "customizing fields" doc itself should probably be moved
to the fields overview section so it remains as intuitive as possible
when searching for this information.
I think some automatic formatter added the `{' '}` styling, it doesn't
render correctly. Update the formatting for this banner to remove it and
correctly refer to the `_status` field.
---------
Signed-off-by: Steve Kuznetsov <stekuznetsov@microsoft.com>
There was a `<br/>` tag, which was visible on the docs page. Also, I
removed spacing from the second tip box, to keep in consistent with tips
in other places in docs.
### What?
This PR fixes numerous links across the docs, both internal docs links
and external links. This PR also fixes some minor formatting issues in
some places, as well as optically aligns the markdown tables in tables
that had broken links.
### Why?
To properly link readers to the correct location in the docs, and for
better formatting and easier consumption.
### How?
Changes to many `.mdx` files in the `docs` folder.
Notes:
- There are duplicative section id's in `docs/authentication/email.mdx`,
I've fixed one such link, but have left it as is for now.
### What?
Previously, setting the `admin.rows` property did not change the height
of the `textarea` field input.
### Why?
Although `rows` was being properly set on the textarea element - it's
absolute positioning prevented the height from actually changing.
### How?
Updates the styles of the textarea field component to properly allow the
rows prop to change the height of the field.
Example w/:
```
{
name: 'someTextArea',
type: 'textarea',
admin: {
rows: 5,
}
}
```
Before:

After:

Fixes#10017
### What?
Allow the join field to have a configuration `on` relationships inside
of an array, ie `on: 'myArray.myRelationship'`.
### Why?
This is a more powerful and expressive way to use the join field and not
be limited by usage of array data. For example, if you have a roles
array for multinant sites, you could add a join field on the sites to
show who the admins are.
### How?
This fixes the traverseFields function to allow the configuration to
pass sanitization. In addition, the function for querying the drizzle
tables needed to be ehanced.
Additional changes from https://github.com/payloadcms/payload/pull/9995:
- Significantly improves traverseFields and the 'join' case with a raw
query injection pattern, right now it's internal but we could expose it
at some point, for example for querying vectors.
- Fixes potential issues with not passed locale to traverseFields (it
was undefined always)
- Adds an empty array fallback for joins with localized relationships
Fixes #
https://github.com/payloadcms/payload/discussions/9643
---------
Co-authored-by: Because789 <thomas@because789.ch>
Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
Rework of https://github.com/payloadcms/payload/pull/5912
### What?
Now, when `defaultValue` is defined as function you can receive the
`req` argument:
```ts
{
name: 'defaultValueFromReq',
type: 'text',
defaultValue: async ({ req, user, locale }) => {
return Promise.resolve(req.context.defaultValue)
},
},
```
`user` and `locale` even though are repeated in `req`, this potentially
leaves some room to add more args in the future without removing them
now.
This also improves type for `defaultValue`:
```ts
type SerializableValue = boolean | number | object | string
export type DefaultValue =
| ((args: {
locale?: TypedLocale
req: PayloadRequest
user: PayloadRequest['user']
}) => SerializableValue)
| SerializableValue
```
### Why?
To access the current URL / search params / Local API and other things
directly in `defaultValue`.
### How?
Passes `req` through everywhere where we call `defaultValue()`
Add the ability to specify which columns should appear in the
relationship table of a join fields
The new property is in the Join field `admin.defaultColumns` and can be
set to an array of strings containing the field names in the desired
order.
### What?
Adds the ability to set custom validation rules on the root `graphQL`
config property and the ability to define custom complexity on
relationship, join and upload type fields.
### Why?
**Validation Rules**
These give you the option to add your own validation rules. For example,
you may want to prevent introspection queries in production. You can now
do that with the following:
```ts
import { GraphQL } from '@payloadcms/graphql/types'
import { buildConfig } from 'payload'
export default buildConfig({
// ...
graphQL: {
validationRules: (args) => [
NoProductionIntrospection
]
},
// ...
})
const NoProductionIntrospection: GraphQL.ValidationRule = (context) => ({
Field(node) {
if (process.env.NODE_ENV === 'production') {
if (node.name.value === '__schema' || node.name.value === '__type') {
context.reportError(
new GraphQL.GraphQLError(
'GraphQL introspection is not allowed, but the query contained __schema or __type',
{ nodes: [node] }
)
);
}
}
}
})
```
**Custom field complexity**
You can now increase the complexity of a field, this will help users
from running queries that are too expensive. A higher number will make
the `maxComplexity` trigger sooner.
```ts
const fieldWithComplexity = {
name: 'authors',
type: 'relationship',
relationship: 'authors',
graphQL: {
complexity: 100, // highlight-line
}
}
```
- Refactoring that simplifies finding things:
```md
## BEFORE
- Rich Text
- Overview
- Slate
- Lexical
- Lexical
- Overview
- Converters
- Migration
- Custom Features
## AFTER
- Rich Text
- Overview
- Converters
- Custom Features
- Migration
- Slate (legacy)
```
- It takes some of the spotlight away from Slate. Lexical is assumed as
the default editor and a banner at the beginning refers to the Slate
documentation.
- Various writing improvements.
PENDING:
- [ ] some 301 redirects needed
- `/docs/rich-text/lexical` to `/docs/rich-text/overview`
- `/docs/lexical/overview` to `/docs/rich-text/overview`
- `/docs/lexical/converters` to `/docs/rich-text/converters`
- `/docs/lexical/migration` to `/docs/rich-text/migration`
### What?
Adds custom anchor tags to docs where duplicate headings exist.
### Why?
Anchor links would not correctly navigate to the proper point on the
page if there were multiple headings with the same string.
### How?
The website now supports adding custom `#anchor` to a heading in
markdown that will attach to the headings and table of content list
items. This PR adds custom anchors to the docs that have duplicate
headings.
**Example:**
```md
/docs/upload/storage-adapters.mdx
### Usage#vercel-blob-installation
```
Generates the path:
`/docs/upload/storage-adapters#vercel-blob-installation`
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>
Adds documentation for `within` and `intersects` operators.
#### Querying - within
In order to do query based on whether points are within a specific area
defined in GeoJSON, you can use the `within` operator.
Example:
```ts
const polygon: Point[] = [
[9.0, 19.0], // bottom-left
[9.0, 21.0], // top-left
[11.0, 21.0], // top-right
[11.0, 19.0], // bottom-right
[9.0, 19.0], // back to starting point to close the polygon
]
payload.find({
collection: "points",
where: {
point: {
within: {
type: 'Polygon',
coordinates: [polygon],
},
},
},
})
```
#### Querying - intersects
In order to do query based on whether points intersect a specific area
defined in GeoJSON, you can use the `intersects` operator.
Example:
```ts
const polygon: Point[] = [
[9.0, 19.0], // bottom-left
[9.0, 21.0], // top-left
[11.0, 21.0], // top-right
[11.0, 19.0], // bottom-right
[9.0, 19.0], // back to starting point to close the polygon
]
payload.find({
collection: "points",
where: {
point: {
intersects: {
type: 'Polygon',
coordinates: [polygon],
},
},
},
})
```
### What?
Adds full support for the point field to Postgres and Vercel Postgres
adapters through the Postgis extension. Fully the same API as with
MongoDB, including support for `near`, `within` and `intersects`
operators.
Additionally, exposes to adapter args:
*
`tablesFilter`https://orm.drizzle.team/docs/drizzle-kit-push#including-tables-schemas-and-extensions.
* `extensions` list of extensions to create, for example `['vector',
'pg_search']`, `postgis` is created automatically if there's any point
field
### Why?
It's essential to support that field type, especially if the postgres
adapter should be out of beta on 3.0 stable.
### How?
* Bumps `drizzle-orm` to `0.36.1` and `drizzle-kit` to `0.28.0` as we
need this change https://github.com/drizzle-team/drizzle-orm/pull/3141
* Uses its functions to achieve querying functionality, for example the
`near` operator works through `ST_DWithin` or `intersects` through
`ST_Intersects`.
* Removes MongoDB condition from all point field tests, but keeps for
SQLite
Resolves these discussions:
https://github.com/payloadcms/payload/discussions/8996https://github.com/payloadcms/payload/discussions/8644
### What?
Moved the logic for copying the data.id to data._id to the mongoose
adapter.
### Why?
If you have any hooks that need to set the `id`, the value does not get
sent to mongodb as you would expect since it was copied before the
beforeValidate hooks.
### How?
Now data._id is assigned only in the mongodb adapter's `create`
function.
BREAKING CHANGES:
When using custom ID fields, if you have any collection hooks for
beforeValidate, beforeChange then `data._id` will no longer be assigned
as this happens now in the database adapter. Use `data.id` instead.
### What?
Makes it possible to filter join documents using a `where` added
directly in the config.
### Why?
It makes the join field more powerful for adding contextual meaning to
the documents being returned. For example, maybe you have a
`requiresAction` field that you set and you can have a join that
automatically filters the documents to those that need attention.
### How?
In the database adapter, we merge the requested `where` to the `where`
defined on the field.
On the frontend the results are filtered using the `filterOptions`
property in the component.
Fixes
https://github.com/payloadcms/payload/discussions/8936https://github.com/payloadcms/payload/discussions/8937
---------
Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
### What?
Includes `hasMany`, `minRows`, and `maxRows` in Upload field config
options table.
### Why?
To be inline with the type definitions.
### How?
Changes to `docs/fields/upload.mdx`
### What?
I noticed a spelling error in the banner of the beta docs and decided I
could save everyone some time by *running the entirety of the beta docs*
through a spellchecker.
### Why?
To fix many spelling and formatting mistakes at once.
### How?
By enabling `edit mode` in my browser and letting the built-in
spellchecker perform its magic (and changing _only_ where it made
sense).
~~Ironically, the original spelling mistake that inspired me to do this
remains unchanged as that is a part of the website repo. [PR for that is
here](https://github.com/payloadcms/website/pull/388).~~
### What?
Adds a way to prevent creating new documents from the admin UI in a join
field.
### Why?
There are two reasons:
1. You want to disable this any time as a feature of your admin user
experience
2. When creating a new document it is not yet possible to create the
relationship, preventing create is necessary for the workflow to make
sense.
### How?
join field has a new admin property called `allowCreate`, can be set to
false. By default the UI will never allow create when the current
document being edited does not yet have an `id`.
Fixes #
#8892
### Before
Even though the document doesn't have an ID yet, the create buttons are
shown which doesn't actually work.

### After
Initial document creation:

Prevented using `allowCreate: false`

### What?
Allow specifying the defaultSort and defaultLimit to use for populating
a join field
### Why?
It is much easier to set defaults rather than be forced to always call
the join query using the query pattern ("?joins[categories][limit]=0").
### How?
See docs and type changes
1
`import type { Field } from 'payload/types'`
to
`import type { Field } from 'payload'`
2
`import { buildConfig } from 'payload/config'`
to
`import { buildConfig } from 'payload'`
3
```
import { SelectInput, useField } from 'payload/components/forms';
import { useAuth } from 'payload/components/utilities';
```
to
`import { SelectInput, useAuth, useField } from '@payloadcms/ui'`
4
uses `import type` for `import type { CollectionConfig } from 'payload'`
Fixes https://github.com/payloadcms/payload/issues/8534
UI fields are now excluded by default from the bulk edit view fields
options.
If you need to have the UI field there, you can provide:
```ts
admin: {
disableBulkEdit: false
}
```