## Description
1. Adds ability to publish a specific individual locale (collections and
globals)
2. Shows published locale in versions list and version comparison
3. Adds new int tests to `versions` test suite
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [X] New feature (non-breaking change which adds functionality)
- [ ] This change requires a documentation update
## Checklist:
- [X] I have added tests that prove my fix is effective or that my
feature works
- [X] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
---------
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
The `generateTitle`, `generateDescription`, `generateURL`, and
`generateImage` functions in the SEO Plugin do not currently receive any
args representing the document's entity. This means that within these
functions, it is currently not possible to discern the _type_ of
document you are working with, i.e. a collection or global. The
underlying problem here was that the request made to execute these
functions was threading through `slug` as `undefined`. This is because
the `DocumentInfoProvider` was failing to thread this prop through
context as the types suggest. Now, these functions receive their
respective `collectionConfig` and `globalConfig`.
```ts
import type { GenerateTitle } from '@payloadcms/plugin-seo/types'
import type { Page } from '@/payload-types'
const generateTitle: GenerateTitle<Page> = ({
doc,
collectionConfig,
globalConfig,
}) => {
return `Website.com — ${doc?.title}`
}
```
Previously, this worked with MongoDB but failed with Postgres / SQLite
when the `slug` field has both `localized: true` and `unique: true`.
```ts
await payload.create({
collection: "posts",
locale: "en",
data: {
slug: "my-post"
}
})
await payload.create({
collection: "posts",
locale: "de",
data: {
slug: "my-post"
}
})
```
Now, we build unique constraints and indexes in combination with the
_locale column. This should also improve query performance for fields
with both index: true and localized: true.
### Migration steps (Postgres/SQLite only)
This change updates the database schema and requires a migration (if you
have any localized fields). To apply it, run the following commands:
```sh
pnpm payload migration:create locale_unique_indexes
pnpm payload migrate
```
Note that if you use `db.push: true` which is a default, you don't have
to run `pnpm payload migrate` in the development mode, only in the
production, as Payload automatically pushes the schema to your DB with
it.
Fixes a case where in relational DBs, you can't duplicate documents if
you have localized arrays within unnamed tabs.
The `beforeDuplicate` hooks were not being run for fields within unnamed
tabs.
This PR fixes querying by a relationship field that has custom IDs in
`relationTo` with different types.
Now, in this case, we do cast the ID value in the database.
Example of the config / int test that reproduced the issue:
```ts
{
slug: 'posts-a',
fields: [],
},
{
slug: 'posts-b',
fields: [],
},
{
slug: 'posts-custom-id',
fields: [{ name: 'id', type: 'text' }],
},
{
slug: 'roots',
fields: [
{
name: 'rel',
relationTo: ['posts-a', 'posts-b', 'posts-custom-id'],
type: 'relationship',
},
],
},
```
```ts
const postA = await payload.create({ collection: 'posts-a', data: {} })
const postB = await payload.create({ collection: 'posts-b', data: {} })
const postC = await payload.create({
collection: 'posts-custom-id',
data: { id: crypto.randomUUID() },
})
const root_1 = await payload.create({
collection: 'roots',
data: {
rel: {
value: postC.id,
relationTo: 'posts-custom-id',
},
},
})
const res_1 = await payload.find({
collection: 'roots',
where: {
'rel.value': { equals: postC.id },
},
})
// COALESCE types integer and character varying cannot be matched
expect(res_1.totalDocs).toBe(1)
```
<!--
For external contributors, please include:
- A summary of the pull request and any related issues it fixes.
- Reasoning for the changes made or any additional context that may be
useful.
Ensure you have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
-->
## Description
Adds `admin.components.header` option to allow users to insert custom
components in the page header / top of page.
[Related
discussion](https://github.com/payloadcms/payload/discussions/7584)
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [ ] Chore (non-breaking change which does not add functionality)
- [X] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [x] This change requires a documentation update
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works - will add
- [X] Existing test suite passes locally with my changes
In order to have beta releases properly trigger GitHub Actions'
`published` event in our `post-release` workflow, this job must exist on
the branch the release is on.
Fixes https://github.com/payloadcms/payload/issues/8111 and
https://github.com/payloadcms/payload/issues/8113
Before: 132 dependencies
After: 123 dependencies
This PR also contains a small performance optimization during telemetry
startup: By using the async `fs.promises.readFile` instead of
`readFileSync` we're not blocking the entire thread anymore and are
allowing other stuff to happen while the file is being read.
Also, in our dependency checker, this moves some variables out of loops,
to the module scope, as they only need to be calculated once.
We have to pin file-type to 19.3.0 and cannot upgrade it further (latest
is 19.5.0). See reasoning in
https://github.com/payloadcms/payload/issues/8111#issuecomment-2348119533
Fixes an issue where duplicating documents in Postgres / SQLite would
crash because of a foreign key constraint / unique ID issue when you
have nested arrays / blocks within localized arrays / blocks.
We now run `beforeDuplicate` against all locales prior to
`beforeValidate` and `beforeChange` hooks.
This PR also fixes a series of issues in Postgres / SQLite where you
have localized groups / named tabs, and then arrays / blocks within the
localized groups / named tabs.
The SEO Plugin defines duplicative translations in both TS and JSON,
even though JSON translations are no longer in use. Translations were
still being maintained in JSON, despite this fact. This PR removes all
JSON files, replacing them with TS, and improving file organization and
overall types.
Ajv 8.14.0 => 8.17.1
- Bundle size: 119.6kB => 111kB
- Dependencies: 5 => 4
- Gets rid of dependency on `punycode`. Will help with the annoying
deprecated module console warning spam
This also upgrades TypeScript to 5.6.2 in our monorepo. The most
type-relevant packages are updated as well, e.g. ts-essentials and
@types/node
Resolves#8172
## Summary
This PR addresses an issue where the`id` field in the GraphQL schema is
incorrectly marked as `nullable`. The change ensures that the `id` field
is set to non-nullable, which aligns with the expectation that every
resource should have a non-nullable ID, especially when using UUIDs as
primary keys.
### Changes
- Fix: Set the `id` field type to `GraphQLNonNull` for consistency in
the GraphQL schema.
This PR changes the type of `selected` returned from the `useSelection`
hook from the `SelectionProvider` from an object to a Map.
This fixes a bug where in some situations we lose the type of the ID
which can break data entry when using postgres, due to keys being cast
to strings inside of objects which doesn't happen when using a Map.
This PR also fixes a CSS bug with the checkbox when it should be
partially selected.
```ts
// before
selected: Record<number | string, boolean>
// after
selected: Map<number | string, boolean>
```
This means you now need to read the data differently than before.
```ts
// before
Object.entries(selected).forEach(([key, value]) => {
// do something
})
// after
for (const [key, value] of selected) {
// do something
}
```
Although the `<FieldLabel />` component receives a `field` prop, it does
not use this prop to extract the `label` from the field. This is
currently only an issue when rendering this component directly, such as
within `admin.components.Label`. The label simply won't appear unless
explicitly provided, despite it being passed as `field.label`. This is
not an issue when rendering field components themselves, because they
properly thread this value through as a top-level prop.
Here's an example of the issue:
```tsx
import type { TextFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const MyCustomLabelComponent: TextFieldLabelServerComponent = ({ clientField }) => {
return (
<FieldLabel
field={clientField}
label={clientField.label} // this should not be needed!
/>
)
}
```
Here is the end result:
```tsx
import type { TextFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const MyCustomLabelComponent: TextFieldLabelServerComponent = ({ clientField }) => {
return <FieldLabel field={clientField} />
}
```
Fixes a type error when using server components for field labels,
descriptions, and errors. The `clientField` prop will always exist, so
the types just need to be reflective of this. Here's an example:
```tsx
import type { TextFieldServerLabelComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const MyServerFieldLabelComponent: TextFieldServerLabelComponent = ({ clientField }) => {
return <FieldLabel field={clientField} /> // `TextFieldClientWithoutType | undefined` is not assignable to type `ClientFieldWithoutType`
}
```
## Description
`singular` labels were not being used for array rows - this PR updates
the array field to properly retrieve the correct label
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
## Description
### TL;DR:
It's currently not possible to render our field components from a server
component because their `field` prop is the original field config, not
the _client_ config which our components require. Currently, the `field`
prop passed into custom fields changes type depending on whether it's a
server or client component, leaving server components without any access
to the client field config or mechanism to acquire it.
This PR passes the client config to all server field components through
a new `clientField` prop. This allows the following in a server
component, which is very similar to how client field components
currently work:
Server component:
```tsx
import { TextField } from '@payloadcms/ui'
import type { TextFieldServerComponent } from 'payload'
export const MyCustomServerField: TextFieldServerComponent = ({ clientField }) => {
return <TextField field={clientField} />
}
```
Client component:
```tsx
'use client'
import { TextField } from '@payloadcms/ui'
import type { TextFieldClientComponent } from 'payload'
export const MyCustomClientField: TextFieldClientComponent = ({ field }) => {
return <TextField field={field} />
}
```
### Full Background
If you have a custom field component, and it's a server component, there
is currently no way to pass the field prop into Payload's client-side
field components.
Here's an example of the problem:
```tsx
import { TextField } from '@payloadcms/ui'
import type { TextFieldServerComponent } from 'payload'
import React from 'react'
export const MyServerComponent: TextFieldServerComponent = (props) => {
const { field } = props
return (
<TextField field={field} /> // This is not possible
)
}
```
The config needs to be transformed into a client config, however,
because of the sheer number of hard-to-find arguments that the
`createClientField` requires, we cannot use it in its raw form.
Here is another example of the problem:
```tsx
import { TextField } from '@payloadcms/ui'
import { createClientField } from '@payloadcms/ui/utilities/createClientField'
import type { TextFieldServerComponent } from 'payload'
import React from 'react'
export const MyServerComponent: TextFieldServerComponent = ({ createClientField }) => {
const clientField = createClientField({...}) // Not a good option bc it requires many hard-to-find args
return (
<TextField field={clientField} />
)
}
```
Theoretically, we could preformat a `createFieldConfig` function so it
can simply be called without arguments:
```tsx
import { TextField } from '@payloadcms/ui'
import type { TextFieldServerComponent } from 'payload'
import React from 'react'
export const MyServerComponent: TextFieldServerComponent = ({ createClientField }) => {
return <TextField field={createClientField()} />
}
```
But this means the field config would be evaluated twice unnecessarily,
including label functions, etc.
The right way to fix this is to simply pass the client config to server
components through a new `clientField` prop:
```tsx
import { TextField } from '@payloadcms/ui'
import type { TextFieldServerComponent } from 'payload'
import React from 'react'
export const MyServerComponent: TextFieldServerComponent = ({ clientField }) => {
return <TextField field={clientField} />
}
```
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## Checklist:
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
## Description
Currently, you cannot create, delete, or duplicate documents within the
document drawer directly. To create a document within a relationship
field, for example, you must first navigate to the parent field and open
the "create new" drawer. Similarly (but worse), to duplicate or delete a
document, you must _navigate to the parent document to perform these
actions_ which is incredibly disruptive to the content editing workflow.
This becomes especially apparent within the relationship field where you
can edit documents inline, but cannot duplicate or delete them. This PR
supports all document-level actions within the document drawer so that
these actions can be performed on-the-fly without navigating away.
Inline duplication flow on a polymorphic "hasOne" relationship:
https://github.com/user-attachments/assets/bb80404a-079d-44a1-b9bc-14eb2ab49a46
Inline deletion flow on a polymorphic "hasOne" relationship:
https://github.com/user-attachments/assets/10f3587f-f70a-4cca-83ee-5dbcad32f063
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
Closes https://github.com/payloadcms/payload/issues/7952
Adds support for `in` and `not_in` operator against JSON field filters.
The following queries are now valid in postgres as well, previously it
only worked in mongo
```ts
await payload.find({
collection: 'posts',
where: {
'data.value': {
in: ['12', '13', '14'],
},
},
context: {
disable: true,
},
})
await payload.find({
collection: 'posts',
where: {
'data.value': {
not_in: ['12', '13', '14'],
},
},
context: {
disable: true,
},
})
```
The `BlockField` type is not representative of the underlying "blocks"
field type, which is plural, i.e. `BlocksField`. This is a semantic
change that will better align the type with the field.
## Breaking Changes
Types related to the `blocks` field have change names. If you were using
the `BlockField` or related types in your own applications, simply
change the import name to be plural and instead of singular.
Old (singular):
```ts
import type {
BlockField,
BlockFieldClient,
BlockFieldValidation,
BlockFieldDescriptionClientComponent,
BlockFieldDescriptionServerComponent,
BlockFieldErrorClientComponent,
BlocksFieldErrorServerComponent,
BlockFieldLabelClientComponent,
BlockFieldLabelServerComponent,
} from 'payload'
```
New (plural):
```ts
import type {
BlocksField,
BlocksFieldClient,
BlocksFieldValidation,
BlocksFieldDescriptionClientComponent,
BlocksFieldDescriptionServerComponent,
BlocksFieldErrorClientComponent,
BlocksFieldErrorServerComponent,
BlocksFieldLabelClientComponent,
BlocksFieldLabelServerComponent,
} from 'payload'
```
## Description
Fixes the bug I reported in
https://github.com/payloadcms/payload/issues/8139 where the casing of
the defined value (camelCase) of Vercel's Postgres database adapter does
not match the casing of the package (kebab-case).
Closes https://github.com/payloadcms/payload/issues/7867
Problem: currently, setting an
```ts
admin: {
width: '30%'
}
```
does not work for fields inside a row or similar (group, array etc.)
Solution: when we render the field, we set a CSS variable
`--field-width` with the value of `admin.width`. This allows us to
calculate the correct width for a field in CSS by doing `flex: 0 1
var(--field-width);`
It also allows us to properly handle `gap` with `flex-wrap: wrap;`
Notes: added playwright tests to ensure widths are correctly rendered

## Description
Fixes https://github.com/payloadcms/payload/issues/8107
This has been confusing for people from countries where characters
aren't latin, for example the Japanese file name:
フェニックス.png
Turns into:
ãã§ããã¯ã¹.png
Additionally, ensures type-safety for `DEFAULT_OPTIONS` and removes
unused `fileHandler` property from there, which isn't defined in the
`FetchAPIFileUploadOptions` type.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
## Description
Fixes https://github.com/payloadcms/payload/issues/6037
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
## Description
Fixes#8038, which was broken in #7817
I'm not entirely sure if this change violates the original intent of the
"base" utility, which from what I understand was introduced for
scalability reasons. Either way, I think it's a good idea to keep the
indent at 40px all the time.
The reason for this is that browsers use 40px as the indentation setting
for lists, and using that setting the indented paragraphs and headings
match the lists. See https://github.com/facebook/lexical/pull/4025
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
## Description
Currently, there is no way of typing custom server field components.
This is because internally, all field components are client components,
and so these were never fully typed. For example, the docs currently
indicate for all custom fields to be typed in this way:
Old:
```tsx
export const MyClientTextFieldComponent: React.FC<TextFieldProps>
```
But if your component is a server component, you will never receive the
fully typed `field` prop, `payload` prop, etc. unless you've typed that
yourself using some of the underlying utilities. So to fix this, every
field now explicitly exports a type for each environment:
New:
- Client component:
```tsx
'use client'
export const MyClientTextFieldComponent: TextFieldClientComponent
```
- Server component:
```tsx
export const MyServerTextFieldComponent: TextFieldServerComponent
```
This pattern applies to every field type, where the field name is
prepended onto the component type.
```ts
import type {
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
TextareaFieldClientComponent,
TextareaFieldServerComponent,
TextareaFieldClientProps,
TextareaFieldServerProps,
// ...and so on for each field type
} from 'payload'
```
## BREAKING CHANGES
We are no longer exporting `TextFieldProps` etc. for each field type.
Instead, we now export props for each client/server environment
explicitly. If you were previously importing one of these types into
your custom component, simply change the import name to reflect your
environment.
Old:
```tsx
import type { TextFieldProps } from 'payload'
```
New:
```tsx
import type { TextFieldClientProps, TextFieldServerProps } from 'payload'
```
Related: #7754.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update
## Checklist:
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
fixes#7762
This change mitigates having multiple preferences for one user but not
awaiting the change to a preference and reduces querying by skipping the
access control. In the event that a user has multiple preferences with
the same key, only the one with the latest updatedAt will be returned.
BREAKING CHANGES:
- payload/preferences/operations are no longer default exports
## Description
<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [ ] Chore (non-breaking change which does not add functionality)
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [ ] This change requires a documentation update
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
---------
Co-authored-by: Paul Popus <paul@nouance.io>
## Description
By default all api requests are creating transactions due to the
authentication stategy. This change removes transactions for auth and
login requests. This should only happen when the database needs to make
changes in which case the auth strategy or login lockout updates will
invoke their own transactions still.
This should improve performance without any sacrifice to database
consistency.
Fixes#8092
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [ ] Chore (non-breaking change which does not add functionality)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [ ] This change requires a documentation update
## 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
This PR fixes and improves:
- ListQuery provider is now the source of truth for searchParams instead
of having components use the `useSearchParams` hook
- Various issues with search params and filters sticking around when
navigating between collections
- Pagination and limits not working inside DocumentDrawer
- Searching and filtering causing a flash of overlay in DocumentDrawer,
this now only shows for the first load and on slow networks
- Preferences are now respected in DocumentDrawer
- Changing the limit now resets your page back to 1 in case the current
page no longer exists
Fixes https://github.com/payloadcms/payload/issues/7085
Fixes https://github.com/payloadcms/payload/pull/8081
Fixes https://github.com/payloadcms/payload/issues/8086
## Description
Uses the `Thumbnail` component used in other places for the bulk upload
file rows. Closes#8099
In the future, we should consider adding different thumbnail icons based
on the `mimeType` to better describe the files being uploaded.
Before:

After:

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## 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
## Description
Updates styling on modals and auth forms for more consistent spacing and
alignment.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
## Description
Currently, the relationship field's _value(s)_ each render and controls
its own document drawer. This has led to `hasMany` relationships
processing a potentially large number of drawers unnecessarily. But the
real problem is when attempting to perform side-effects as a result of a
drawer action. Currently, when you change the value of a relationship
field, all drawers within are (rightfully) unmounted because the
component representing the value was itself unmounted. This meant that
you could not update the title of a document, for example, then update
the underlying field's value, without also closing the document drawer
outright. This is needed in order to support things like creating and
duplicating documents within document drawers (#7679).
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
## Description
Fixes https://github.com/payloadcms/payload/issues/7109
Example of table structures that lead to the problem with camelCased
group / tab names.
`group_field_array_localized` - `groupField` -> `array` (has a localized
field inside)
`group_field_array_nested_array` - `groupField` -> `array` ->
`nestedArray`
<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
Previously, on some machines this command:
`pnpx create-payload-app@beta app` created a project using `npm`,
instead of `pnpm`, the same with `yarn`.
Also, the way we detected the package manager was always prioritizing
`pnpm`, even if they executed the command with `yarn` / `npm`. Now we
are relying only on from which package manager user executed
`create-payload-app`.
The code for detection is grabbed from create-next-app
https://github.com/vercel/next.js/blob/canary/packages/create-next-app/helpers/get-pkg-manager.ts
## Description
Without using `React.FC<>`, the type needs to be placed on the right
side of the props object.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Chore (non-breaking change which does not add 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~
- [x] I have made corresponding changes to the documentation
## Description
Reduces the number of client-side requests made by the relationship
field component, and fixes the visual "blink" of the field's value on
initial load. Does so through a new `useIgnoredEffect` hook that allows
this component's effects to be precisely triggered based on whether a
only _subset_ of its dependencies have changed, which looks something
like this:
```tsx
// ...
useIgnoredEffect(() => {
// Do something
}, [deps], [ignoredDeps])
```
"Ignored deps" are still treated as normal dependencies of the
underlying `useEffect` hook, but they do not cause the provided function
to execute. This is useful if you have a list of dependencies that
change often, but need to scope your effect's logic to explicit
dependencies within that list. This is a typical pattern in React using
refs, just standardized within a reusable hook.
This significantly reduces the overall number of re-renders and
duplicative API requests within the relationship field because the
`useEffect` hooks that control the fetching of these related documents
were running unnecessarily often. In the future, we really ought to
leverage the `RelationshipProvider` used in the List View so that we can
also reduce the number of duplicative requests across _unrelated fields_
within the same document.
Before:
https://github.com/user-attachments/assets/ece7c85e-20fb-49f6-b393-c5e9d5176192
After:
https://github.com/user-attachments/assets/9f0a871e-f10f-4fd6-a58b-8146ece288c4
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
We noticed that we can bring functions down to the client directly
without having to wrap them in a component first. This greatly
simplifies the loading of all lexical client components
**BREAKING:**
- `createClientComponent` is no longer exported as it's not needed
anymore
- The exported `ClientComponentProps` type has been renamed to
`BaseClientFeatureProps`.
- The order of arguments in `sanitizeClientEditorConfig` has changed
Removes `loggerOptions` and `loggerDestination` from `initOptions`
(these were not able to be used anyway).
Creates new `logger` property on the Payload config.
```ts
// Logger options only
logger: {
level: 'info',
}
// Logger options with destination stream
logger: {
options: {
level: 'info',
},
destination: process.stdout
},
// Logger instance
logger: pino({ name: 'my-logger' })
```
Now enforcing curly brackets on all if statements. Includes auto-fixer.
```ts
// ❌ Bad
if (foo) foo++;
// ✅ Good
if (foo) {
foo++;
}
```
Note: this did not lint the `drizzle` package or any `db-*` packages.
This will be done in the future.
## Description
Payload localization works on a field-by-field basis. As you can nest
fields within other fields, you could potentially nest a localized field
within a localized field—but this would be redundant and unnecessary.
There would be no reason to define a localized field within a localized
parent field, given that the entire data structure from the parent field
onward would be localized.
Up until this point, Payload would _allow_ you to nest a localized field
within another localized field, and this might have worked in MongoDB
but it will throw errors in Postgres.
Now, Payload will automatically remove the `localized: true` property
from sub-fields within `sanitizeFields` if a parent field is localized.
This could potentially be a breaking change if you have a configuration
with MongoDB that nests localized fields within localized fields.
## Migrating
You probably only need to migrate if you are using MongoDB, as there,
you may not have noticed any problems. But in Postgres or SQLite, this
would have caused issues so it's unlikely that you've made it too far
without experiencing issues due to a nested localized fields config.
In the event you would like to keep existing data in this fashion, we
have added a `compatibility.allowLocalizedWithinLocalized` flag to the
Payload config, which you can set to `true`, and Payload will then
disable this new sanitization step.
Set this compatibility flag to `true` only if you have an existing
Payload MongoDB database from pre-3.0, and you have nested localized
fields that you would like to maintain without migrating.
Move `ui` and `translations` from peerDeps into deps for a few packages.
Users should not have to install these directly unless they are making
customizations.
fixes https://github.com/payloadcms/payload/issues/7379
The enabledCollections and disabledCollections properties of the
RelationshipFeature were not being sent to the client and therefore did
not have the expected effect.
Now those 2 properties are sent to the client via the
`clientFeatureProps` property.
## Description
In Postgres, localized blocks or arrays that contain other array / block
/ relationship fields were not properly storing locales in the database.
Now they are! Need to check a few things yet:
- Ensure test coverage is sufficient
- Test localized array, with non-localized array inside of it
- Test localized block with relationship field within it
- Ensure `_rels` table gets the `locale` column added if a single
non-localized relationship exists within a localized array / block
Fixes step 6 as identified in #7805
## Description
Fixes a race condition where you could switch locales and have autosave
trigger with old locale data.
By adding the `key` to the `Document` component, we will ensure that the
entire `Document` will be un-mounted and re-mounted between locale
switches.
Fixes:
- issue with publish button double saving on keyboard command
- versions diffs not showing if fields are tabs
https://github.com/payloadcms/payload/issues/7860
- navigation on versions not working for perPage and pagination
Fixes https://github.com/payloadcms/payload/issues/7477
This simplifies validation to the point where it only errors on spaces.
Actual validation is then used in beforeChange, which then automatically
url encodes the input if it doesn't pass
`getPayloadHMR`'s arg type was accepting unnecessary args that did not
do anything. This was leading to confusion.
This PR trims down the accepted type.
Fixes#7832
## Description
- Improves the standard typography styles of the rich text editors.
- Improve styles of Lexical relationship, inline-relationship, upload,
and blocks features.
- Improves drag and drop interaction for Lexical.
- Adds a dark mode style for Lexical inline toolbar, floating link editor,
and slash menu.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Chore (non-breaking change which does not add 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
This fixes that type in the website template:
3d86bf1974/templates/website/src/app/components/RichText/serialize.tsx (L24)
Now, JsonObject still ensures that only objects can be passed, but it's
weak enough to allow non-dynamic types like the ones we generate in
payload-types.
The "JSON" part of this type has no meaning anymore, as it does allow
objects with functions now. However, we can still use it to signal to
the user that this should be JSON-serializable. It's more clear than
just using Record<string, unknown>
Dedicated adapter for Vercel Postgres
- Uses the `@vercel/postgres` package under the hood.
- No `pg` dependency, speeds up invocation
- Includes refactoring all base postgres functionality into a
`BasePostgresAdapter` type, which will ease implementation of [other
adapters supported by
drizzle-orm](https://orm.drizzle.team/docs/get-started-postgresql)
## Usage
```ts
import { buildConfig } from 'payload'
import { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'
export default buildConfig({
db: vercelPostgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI,
},
}),
// ...rest of config
})
```
### Automatic Connection String Detection
Have Vercel automatically detect from environment variable (typically
`process.env.POSTGRES_URL`)
```ts
export default buildConfig({
db: postgresAdapter(),
// ...rest of config
})
```
Supports `hasMany` upload fields, similar to how `hasMany` works in
other fields, i.e.:
```ts
{
type: 'upload',
relationTo: 'media',
hasMany: true
}
```
---------
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
## Description
Adds bulk upload functionality to upload enabled configs.
You can disable the ability by defining `upload.bulkUpload: false` in
your upload enabled config.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] 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
Fixes https://github.com/payloadcms/payload/issues/7741
I have no idea why it broke and was not able to reproduce this at all.
But given the amount of people reporting this issue, it's not worth
keeping this around for the small benefit this brings
## Description
Fixes an issue where Block component section titles were taking up the
entire clickable area of block headers.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
## Description
Closes#7784 by properly handling custom collection description
components via `admin.components.Description`. This component was
incorrectly added to the `admin.components.edit` key, and also was never
handled on the front-end. This was especially misleading because the
client-side config had a duplicative key in the proper position.
## Breaking Changes
This PR is only labeled as a breaking change because the key has changed
position within the config. If you were previously defining a custom
description component on a collection, simply move it into the correct
position:
Old:
```ts
{
admin: {
components: {
edit: {
Description: ''
}
}
}
}
```
New:
```ts
{
admin: {
components: {
Description: ''
}
}
}
```
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] This change requires a documentation update
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
## Description
Fixes text clipping that occurs on the document header title when Segoe
UI font is used in the admin panel.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
## Description
Threads the field config to all "field subcomponents" through props,
i.e. field label, description, error, etc. This way, the field config
that controls any particular component is easily accessible and strongly
typed, i.e. `props.field.maxLength`. This is true for both server and
client components, whose server-side props are now also contextually
typed. This behavior was temporarily removed in #7474 due to bloating
HTML, but has since been resolved in #7620. This PR also makes
significant improvements to component types by exporting explicit types
for _every component of every field_, each with its own client/server
variation. Now, a custom component can look something like this:
```tsx
import type { TextFieldLabelServerComponent } from 'payload'
import React from 'react'
export const CustomLabel: TextFieldLabelServerComponent = (props) => {
return (
<div>{`The max length of this field is: ${props?.field?.maxLength}`}</div>
)
}
```
The following types are now available:
```ts
import type {
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldLabelClientComponent,
TextFieldLabelServerComponent,
TextFieldDescriptionClientComponent,
TextFieldDescriptionServerComponent,
TextFieldErrorClientComponent,
TextFieldErrorServerComponent,
// ...and so one for each field
} from 'payload'
```
BREAKING CHANGES:
In order to strictly type these components, a few breaking changes have
been made _solely to type definitions_. This only effects you if you are
heavily using custom components.
Old
```ts
import type { ErrorComponent, LabelComponent, DescriptionComponent } from 'payload'
```
New:
```ts
import type {
FieldErrorClientComponent,
FieldErrorServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
// Note: these are the generic, underlying types of the more stricter types described above ^
// For example, you should use the type that is explicit for your particular field and environment
// i.e. `TextFieldLabelClientComponent` and not simply `FieldLabelClientComponent`
} from 'payload'
```
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
This noticeably improves performance in the admin panel, for example
when there are multiple richtext editors on one page (& likely
performance in other areas too, though I mainly tested rich text).
The babel plugin currently only optimizes files with a 'use client'
directive at the top - thus we have to make sure to add use client
wherever possible, even if it's imported by a parent client component.
There's one single component that broke when it was compiled using the
React compiler (it stopped being reactive and failed one of our admin
e2e tests):
150808f608
opting out of it completely fixed that issue
Fixes https://github.com/payloadcms/payload/issues/7366
Example: richText editor 1 and 2 both have UploadFeature. richText
editor 1 calls UploadFeature() with custom fields, richText editor 2
calls UploadFeature() with NO custom fields. Before this PR, richText
editor 1 would not have had any custom fields, as richText editor 2 will
override the feature object (specifically its props).
## Description
Prevents tabs fields from displaying vertical scrollbars in certain
cases with different viewports/zoom levels.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
Old blank templates had invalid pregenerated importMap. Would error for
fresh apps from create-payload-app. And website was on an old version
riddled with bugs
Also a nice performance improvement. The list drawer was previously
fetching data with depth 1. This will cause the relationship cell to
break, as it expects the relationship data to be a string/number, not a
populated object with the id inside.
Now, it fetches using depth 0 - same as the normal list view
## Description
Minor admin panel style updates:
- Adjusts document header title spacing.
- Makes toast notifications more apparent.
- Adjusts alignment of create new button.
- Improves chevron icon.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Chore (non-breaking change which does not add functionality)
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
## Description
Tweaks to Upload and Dropzone components, making them more extendable.
- Dropzone adds prop to allow multiple files
- Upload correctly sets url if state is initialized with a File
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Chore (non-breaking change which does not add 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
Bumps the github_actions group with 1 update in the
/.github/actions/setup directory:
[pnpm/action-setup](https://github.com/pnpm/action-setup).
Updates `pnpm/action-setup` from 3 to 4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pnpm/action-setup/releases">pnpm/action-setup's
releases</a>.</em></p>
<blockquote>
<h2>v4.0.0</h2>
<p>An error is thrown if one version of pnpm is specified in the
<code>packageManager</code> field of <code>package.json</code> and a
different version is specified in the action's settings <a
href="https://redirect.github.com/pnpm/action-setup/pull/122">#122</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fe02b34f77"><code>fe02b34</code></a>
docs: bump action-setup version in README</li>
<li><a
href="bee1f099e5"><code>bee1f09</code></a>
feat: throw error when multiple versions specified (<a
href="https://redirect.github.com/pnpm/action-setup/issues/122">#122</a>)</li>
<li><a
href="ce859e384f"><code>ce859e3</code></a>
refactor: replace <code>fs-extra</code> with Node.js built-in fs methods
(<a
href="https://redirect.github.com/pnpm/action-setup/issues/120">#120</a>)</li>
<li><a
href="2ab6dce4f5"><code>2ab6dce</code></a>
docs(README): fix link to LICENSE</li>
<li><a
href="e280758d01"><code>e280758</code></a>
docs(README): update dependency versions (<a
href="https://redirect.github.com/pnpm/action-setup/issues/117">#117</a>)</li>
<li><a
href="129abb77bf"><code>129abb7</code></a>
Bump undici from 5.28.2 to 5.28.3 (<a
href="https://redirect.github.com/pnpm/action-setup/issues/115">#115</a>)</li>
<li>See full diff in <a
href="https://github.com/pnpm/action-setup/compare/v3...v4">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates no-relative-monorepo-import regex to handle more scenarios:
❌ Scenarios that will violate the rule:
```ts
import { something } from '../../payload/src/utilities/some-util.js'
import { something } from '../../../packages/payload/src/utilities/some-util.js'
import { something } from 'packages/payload/src/utilities/some-util.js'
```
Bumps the github_actions group with 2 updates:
[pnpm/action-setup](https://github.com/pnpm/action-setup) and
[supercharge/mongodb-github-action](https://github.com/supercharge/mongodb-github-action).
Updates `pnpm/action-setup` from 3 to 4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pnpm/action-setup/releases">pnpm/action-setup's
releases</a>.</em></p>
<blockquote>
<h2>v4.0.0</h2>
<p>An error is thrown if one version of pnpm is specified in the
<code>packageManager</code> field of <code>package.json</code> and a
different version is specified in the action's settings <a
href="https://redirect.github.com/pnpm/action-setup/pull/122">#122</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fe02b34f77"><code>fe02b34</code></a>
docs: bump action-setup version in README</li>
<li><a
href="bee1f099e5"><code>bee1f09</code></a>
feat: throw error when multiple versions specified (<a
href="https://redirect.github.com/pnpm/action-setup/issues/122">#122</a>)</li>
<li><a
href="ce859e384f"><code>ce859e3</code></a>
refactor: replace <code>fs-extra</code> with Node.js built-in fs methods
(<a
href="https://redirect.github.com/pnpm/action-setup/issues/120">#120</a>)</li>
<li><a
href="2ab6dce4f5"><code>2ab6dce</code></a>
docs(README): fix link to LICENSE</li>
<li><a
href="e280758d01"><code>e280758</code></a>
docs(README): update dependency versions (<a
href="https://redirect.github.com/pnpm/action-setup/issues/117">#117</a>)</li>
<li><a
href="129abb77bf"><code>129abb7</code></a>
Bump undici from 5.28.2 to 5.28.3 (<a
href="https://redirect.github.com/pnpm/action-setup/issues/115">#115</a>)</li>
<li>See full diff in <a
href="https://github.com/pnpm/action-setup/compare/v3...v4">compare
view</a></li>
</ul>
</details>
<br />
Updates `supercharge/mongodb-github-action` from 1.10.0 to 1.11.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/supercharge/mongodb-github-action/releases">supercharge/mongodb-github-action's
releases</a>.</em></p>
<blockquote>
<p>Release 1.11.0</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/supercharge/mongodb-github-action/blob/main/CHANGELOG.md">supercharge/mongodb-github-action's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/superchargejs/mongodb-github-action/compare/v1.10.0...v1.11.0">1.11.0</a>
- 2024-05-22</h2>
<h3>Added</h3>
<ul>
<li>added <code>mongodb-container-name</code> input: this option allows
you to define the Docker container name</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>use the <code>mongo</code> command to interact with MongoDB versions
4.x or lower. Previously, we only checked for MongoDB 4 and would use
<code>mongosh</code> for MongoDB 3 (and lower). <a
href="https://redirect.github.com/supercharge/mongodb-github-action/pull/61">Thanks
to Aravind!</a></li>
</ul>
<h3>Updated</h3>
<ul>
<li>bump dependencies</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5a87bd81f8"><code>5a87bd8</code></a>
prepare changelog for 1.11.0</li>
<li><a
href="7c12fc679c"><code>7c12fc6</code></a>
update readme</li>
<li><a
href="ad73029553"><code>ad73029</code></a>
bump mongoose dependency</li>
<li><a
href="268fb2c93c"><code>268fb2c</code></a>
Merge pull request <a
href="https://redirect.github.com/supercharge/mongodb-github-action/issues/61">#61</a>
from aravindnc/main</li>
<li><a
href="12b898a9c8"><code>12b898a</code></a>
Fix to use mongo client if MongoDB verison is less than or equal to
4.</li>
<li><a
href="b8277548e0"><code>b827754</code></a>
wait 20 seconds</li>
<li><a
href="5f37c5fb42"><code>5f37c5f</code></a>
revert ESLint to 8.x</li>
<li><a
href="fcc7443a6b"><code>fcc7443</code></a>
bump verions</li>
<li><a
href="fde299bc70"><code>fde299b</code></a>
bump deps</li>
<li><a
href="9ceda80ede"><code>9ceda80</code></a>
bump versions of GitHub Actions</li>
<li>Additional commits viewable in <a
href="https://github.com/supercharge/mongodb-github-action/compare/1.10.0...1.11.0">compare
view</a></li>
</ul>
</details>
<br />
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
If it's undefined/null => Fallback Component may be rendered
If it's false => No component should be rendered - as if an empty
component was passed in
This ensures that the user does not have to install `@payloadcms/ui`
anymore, which previously exported an empty component to be used in
component paths
Fixes https://github.com/payloadcms/payload/issues/7677
- Payload bin scripts were not properly working on windows
- Use tsx by default instead of swc, as swc does not handle next/cache
imports without the .js at the end
- Support other node runtimes through --disable-transpile flag
## Description
We've since lost the ability to override the document view at the
root-level. This was a feature that made it possible to override _the
entire document routing/view structure_, including the document
header/tabs and all nested routes within, i.e. the API route/view, the
Live Preview route/view, etc. This is distinct from the "default" edit
view, which _only_ targets the component rendered within the "edit" tab.
This regression was introduced when types were simplified down to better
support "component paths" here: #7620. The `default` key was incorrectly
used as the "root" view override. To continue to support stricter types
_and_ root view overrides, a new `root` key has been added to the
`views` config.
You were previously able to do this:
```tsx
import { MyComponent } from './MyComponent.js'
export const MyCollection = {
// ...
admin: {
views: {
Edit: MyComponent
}
}
}
```
This is now done like this:
```tsx
export const MyCollection = {
// ...
admin: {
views: {
edit: {
root: {
Component: './path-to-my-component.js'
}
}
}
}
}
```
Some of the documentation was also incorrect according to the new
component paths API.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] This change requires a documentation update
## Checklist:
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
- regenerates the lockfile
- upgrades pnpm from v8 to v9.7.0 minimum
- ensures playwright does not import payload config. Even after our
importmap revamp that made the payload config server-only / node-safe, I
was getting these `Error: Invariant: AsyncLocalStorage accessed in
runtime where it is not available` errors in combination with pnpm v9
and lockfile regeneration.
This does not happen with pnpm v8, however I'm still blaming playwright
for this, as this does not happen in dev and we've had this specific
error with playwright in the past when we were importing the payload
config. Perhaps it's related to both playwright and the future Next.js
process importing the same config file, and not related to the config
file containing client-side React code.
Making sure playwright doesn't import the config fixed it (it was
importing it through the import map generation). The import map
generation is now run in a separate process, and playwright simply waits
for it
- One positive thing: this pr fixes a bunch of typescript errors with
react-select components. We got those errors because react-select types
are not compatible with react 19. lockfile regeneration fixed that (not
related to pnpm v9) - probably because we were installing mismatching
react versions (I saw both `fb9a90fa48-20240614` and `06d0b89e-20240801`
in our lockfile). I have thus removed the caret for react and react-dom
in our package.json - now it's consistent
This lowers the module count by 31 modules
BREAKING: Migration-related lexical modules are now exported from
`@payloadcms/richtext-lexical/migrate` instead of
`@payloadcms/richtext-lexical`
Allow a compound index to be used for upload collections via a
`filenameCompoundIndex` field. Previously, `filename` was always treated
as unique.
Usage:
```ts
{
slug: 'upload-field',
upload: {
// Slugs to include in compound index
filenameCompoundIndex: ['filename', 'alt'],
},
}
```
This PR makes three major changes to the codebase:
1. [Component Paths](#component-paths)
Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938
2. [Client Config](#client-config)
Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).
3. [Custom client component are no longer
server-rendered](#custom-client-components-are-no-longer-server-rendered)
Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.
## Component Paths
Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938
In order to reference any custom components in the Payload config, you
now have to specify a string path to the component instead of importing
it.
Old:
```ts
import { MyComponent2} from './MyComponent2.js'
admin: {
components: {
Label: MyComponent2
},
},
```
New:
```ts
admin: {
components: {
Label: '/collections/Posts/MyComponent2.js#MyComponent2', // <= has to be a relative path based on a baseDir configured in the Payload config - NOT relative based on the importing file
},
},
```
### Local API within Next.js routes
Previously, if you used the Payload Local API within Next.js pages, all
the client-side modules are being added to the bundle for that specific
page, even if you only need server-side functionality.
This `/test` route, which uses the Payload local API, was previously 460
kb. It is now down to 91 kb and does not bundle the Payload client-side
admin panel anymore.
All tests done
[here](https://github.com/payloadcms/payload-3.0-demo/tree/feat/path-test)
with beta.67/PR, db-mongodb and default richtext-lexical:
**dev /admin before:**

**dev /admin after:**

---
**dev /test before:**

**dev /test after:**

---
**build before:**

**build after::**

### Usage of the Payload Local API / config outside of Next.js
This will make it a lot easier to use the Payload config / local API in
other, server-side contexts. Previously, you might encounter errors due
to client files (like .scss files) not being allowed to be imported.
## Client Config
Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).
This is breaking change. The `useComponentMap` hook no longer exists,
and most component props have changed (for the better):
```ts
const { componentMap } = useComponentMap() // old
const { config } = useConfig() // new
```
The `useConfig` hook has also changed in shape, `config` is now a
property _within_ the context obj:
```ts
const config = useConfig() // old
const { config } = useConfig() // new
```
## Custom Client Components are no longer server rendered
Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.
The benefit of this change:
Custom client components can now receive props. Previously, the only way
for them to receive dynamic props from a parent client component was to
use hooks, e.g. `useFieldProps()`. Now, we do have the option of passing
in props to the custom components directly, if they are client
components. This will be simpler than having to look for the correct
hook.
This makes rendering them on the client a little bit more complex, as
you now have to check if that component is a server component (=>
already has been rendered) or a client component (=> not rendered yet,
has to be rendered here). However, this added complexity has been
alleviated through the easy-to-use `<RenderMappedComponent />` helper.
This helper now also handles rendering arrays of custom components (e.g.
beforeList, beforeLogin ...), which actually makes rendering custom
components easier in some cases.
## Misc improvements
This PR includes misc, breaking changes. For example, we previously
allowed unions between components and config object for the same
property. E.g. for the custom view property, you were allowed to pass in
a custom component or an object with other properties, alongside a
custom component.
Those union types are now gone. You can now either pass an object, or a
component. The previous `{ View: MyViewComponent}` is now `{ View: {
Component: MyViewComponent} }` or `{ View: { Default: { Component:
MyViewComponent} } }`.
This dramatically simplifies the way we read & process those properties,
especially in buildComponentMap. We can now simply check for the
existence of one specific property, which always has to be a component,
instead of running cursed runtime checks on a shared union property
which could contain a component, but could also contain functions or
objects.


- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
---------
Co-authored-by: PatrikKozak <patrik@payloadcms.com>
Co-authored-by: Paul <paul@payloadcms.com>
Co-authored-by: Paul Popus <paul@nouance.io>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
## Description
Fixes#7529
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
Form Builder Plugin BeforeEmail hook now takes a generic for your
generated types and it has the full hook params available to it.
```ts
import type { BeforeEmail } from '@payloadcms/plugin-form-builder'
// Your generated FormSubmission type
import type {FormSubmission} from '@payload-types'
// Pass it through and 'data' or 'originalDoc' will now be typed
const beforeEmail: BeforeEmail<FormSubmission> = (emailsToSend, beforeChangeParams) => {
// modify the emails in any way before they are sent
return emails.map((email) => ({
...email,
html: email.html, // transform the html in any way you'd like (maybe wrap it in an html template?)
}))
}
```
## Description
V2 PR [here](https://github.com/payloadcms/payload/pull/7565)
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
You can now add a redirect type to your redirects if needed:
```ts
// Supported types
redirectTypes: ['301', '302'],
// Override the select field
redirectTypeFieldOverride: {
label: 'Redirect Type (Overridden)',
},
```
## Description
- Improves mobile styling of Payload admin UI.
- Reduces font size on dashboard cards.
- Improves the block/collapsible/array field styling.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Chore (non-breaking change which does not add functionality)
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
You can now override the apiKey field with access control by adding this
field to your auth collection:
```ts
{
name: 'apiKey',
type: 'text',
access: {
update: ({ req }) => req.user.role === 'admin',
}
}
```
Translated labels are now also supported.
Note that `siblingData` isn't working still in FieldAccess control and
`data` only works in non-dynamic fields, eg. fields not in an array or
block for now.
## Description
Fixes the local strategy user lookup.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] 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
## Description
Closes#7524
The query path is overwritten as an empty string in the
`getLocalizedPaths()` function - then when it should throw an invalid
path error it no longer has this info.
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [ ] Chore (non-breaking change which does not add functionality)
- [X] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [ ] This change requires a documentation update
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [X] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
## Description
Fixes#7492
In order to run createMigration, we need to read in the previous
snapshot file if one exists. When that snapshot was generated from an
older version of drizzle-kit, we have to first migrate it up match the
latest version for drizzle to generate the new migration. This change
adds in the call to check the version and migrate the snapshot if
needed.
Fixes https://github.com/payloadcms/payload/issues/6823
Allows the server to initialize the AuthProvider via props. Renames
`HydrateClientUser` to `HydrateAuthProvider`. It now only hydrates the
permissions as the user can be set from props. Permissions can be
initialized from props, but still need to be hydrated for some pages as
access control can be specific to docs/lists etc.
**BREAKING CHANGE**
- Renames exported `HydrateClientUser` to `HydrateAuthProvider`
We are now bumping up the Next canary version to `15.0.0-canary.104` and
`react` and `react-dom` to `^19.0.0-rc-06d0b89e-20240801`.
Your new dependencies should look like this:
```
"next": "15.0.0-canary.104",
"react": "^19.0.0-rc-06d0b89e-20240801",
"react-dom": "^19.0.0-rc-06d0b89e-20240801",
```
---------
Co-authored-by: Alessio Gravili <alessio@gravili.de>
LivePreview data was stale if the user entered data while the socket
connection was being established. This change ensures fresh data is
fetched after the connection is established.
This is easy to see when turning on 4G connection and in CI, where it is
especially slow.
Fixes https://github.com/payloadcms/payload/issues/7495
When the Upload HTML Converter was called from the local API, the upload
document did not populate properly due to overrideAccess not being
passed through to the dataloader. This PR also adds new properties to
the afterRead field hook, so that these can be used in the lexical html
field.
Reproduction here:
https://github.com/payloadcms/payload/tree/chore/reproduce-html-converter-issue
**BREAKING:** If you define your own, custom lexical HTML Converters
that have sub-nodes, or if you directly call the
`convertLexicalNodesToHTML` function anywhere, you now need to pass
through the `showHiddenFields`, draft and `overrideAccess` props to the
`convertLexicalNodesToHTML` function. These are available in the
arguments of your HTML Converter function
## Description
Issue reported by Trading Point.
Payload favicon is still shown even when a custom icon is provided.
To replicate add to Payload config:
```ts
admin: {
meta: {
icons: [
{
url: '/images/test.jpg',
fetchPriority: 'high',
sizes: '16x16',
},
],
},
},
```
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [X] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [X] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
Fixes https://github.com/payloadcms/payload/issues/7428
Now email and username fields are rendered with the RenderFields
component, making them behave similarly to other fields. They now appear
and can respect doc permissions, readOnly settings, etc.
This PR
- upgrades lexical and ports all bug fixes from the playground over
- adds table action buttons. When hovering the edges of the table,
buttons pop up to easily add a new table column or row
- adds an html converter for the table feature
- makes the placeholder shown in the editor when no text is present
accessible
**BREAKING:** This upgrades lexical from 0.16.1 to 0.17.0. If you have
any lexical packages installed in your project, please update them
accordingly. Additionally, if you depend on the lexical APIs, please
consult their changelog, as lexical may introduce breaking changes:
https://github.com/facebook/lexical/releases/tag/v0.17.0
## Description
Fixes#7354
Since the `defaultIDType` for IDs in `postgres` are of type `number` -
the `contains` operator should be available in the filter options.
This PR checks the `defaultIDType` of ID and properly outputs the
correct component type for IDs
I.e if ID is of type `number` - the filter operators for ID should
correspond to the the operators of type number as well
The `contains` operator only belongs on fields of type string, aka of
component type `text`
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
Allows username to be optional when using the new loginWithUsername
feature. This can be done by the following:
```ts
auth: {
loginWithUsername: {
requireUsername: false, // <-- new property, default true
requireEmail: false, // default: false
allowEmailLogin: true, // default false
},
},
```
## Description
`const localeValues = locales.map((locale) => locale.value)`
This line was previously throwing an error in the version view when
localization is false. Changed to ensure locales exist before mapping
over them.
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [ ] Chore (non-breaking change which does not add functionality)
- [X] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [ ] This change requires a documentation update
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [X] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
## Description
- Updates admin UI with more condensed spacing throughout.
- Improves hover states and read-only states for various components.
- Removes the `Merriweather` font from `next/font` and replaces with
stack of system serif fonts and fallbacks (Georgia, etc). Closes#7257
## BREAKING CHANGES
- Custom components and styling that don't utilize Payload's CSS/SCSS
variables may need adjustments to match the updated styling.
- If you are using the `Merriweather` font, you will need to manually
configure `next/font` in your own project.
---------
Co-authored-by: Paul Popus <paul@nouance.io>
fix#4990 (v3)
## Description
Expose
[useTableColumns](b160686fff/packages/ui/src/elements/TableColumns/index.tsx (L25))
hook from client exported members of the ui packages.
The use of this hook, covered the case of custom ListView creation which
was not possible due to the lack of possibility to select a file if we
were in the "list-draw" view.
With `useTableColumns` we can execute the `onClick` defined in
`TableColumnsProvider` witch allows the selection on the clicked file.
b160686fff/packages/ui/src/elements/ListDrawer/DrawerContent.tsx (L290-L296)
## Use case
CustomListView.tsx:
```ts
const CustomListView = () => {
// ...
const tableColumns = useTableColumns()
const handleItemClicked = (doc) => {
const onClick = tableColumns.columns[0].cellProps?.onClick
if (typeof onClick === 'function') {
// we are in "list-drawer" view, execute the onClick function
onClick({
cellData: undefined,
collectionSlug: doc,
rowData: doc,
})
} else {
// we are in "collection-admin" view, push the new route with next/navigation
void router.push(`${collectionSlug}/${doc.id}`)
}
}
return <div className={"list"}>
{data.docs?.length > 0 && (
<RelationshipProvider>
{docs.map((e, i) => (
<div className={"item"} key={i} onClick={() => handleItemClicked(e)}>
// ...
</div>
))}
</RelationshipProvider>
)}
</div>
}
```
This video shows the click of a file inside a CustomListView, in the
case of an "admin-collection" view then a "list-drawer" view.
https://github.com/user-attachments/assets/8aa17af5-a7aa-49de-b988-fc0db7ac8e47
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Chore (non-breaking change which does not add functionality)
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
## Description
Closes#7488
Note - you'll also need to manually have `@libsql/client` installed in
your Next.js repository. This is not ideal, but it might be outside the
scope of what we can handle internally.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Description
https://github.com/payloadcms/payload/pull/5015 's version for beta
branch. @JessChowdhury
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [X] New feature (non-breaking change which adds functionality)
- [X] This change requires a documentation update
## Checklist:
- [X] I have added tests that prove my fix is effective or that my
feature works
- [X] Existing test suite passes locally with my changes
- [X] I have made corresponding changes to the documentation
## Description
Nav items not displaying different style when active.
We were previously using `NavLink` which determines if the item is
active and applies the classname. Now we are using the standard `Link`
and need to add the `active` classname manually.
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [ ] Chore (non-breaking change which does not add functionality)
- [X] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [ ] This change requires a documentation update
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [X] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
## Description
Adds option to restore a version as a draft.
1. Run `versions` test suite
2. Go to `drafts` and choose any doc with `status: published`
3. Open the version
4. See new `restore as draft` option
<img width="1693" alt="Screenshot 2024-07-12 at 1 01 17 PM"
src="https://github.com/user-attachments/assets/14d4f806-c56c-46be-aa93-1a2bd04ffd5c">
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [ ] Chore (non-breaking change which does not add functionality)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [ ] This change requires a documentation update
## Checklist:
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [X] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
Fixes https://github.com/payloadcms/payload/issues/7380
Adjusts how the password/confirm-password fields are validated. Moves
validation to the server, adds them to a custom schema under the schema
path `${collectionSlug}.auth` for auth enabled collections.
## Description
Fixes issue where the `basePath` from the `next-config` was not
respected for the `logout` button link
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
## Description
Prior to this change, the `defaultValue` for fields have only been used
in the application layer of Payload. With this change, you get the added
benefit of having the database columns get the default also. This is
especially helpful when adding new columns to postgres with existing
data to avoid needing to write complex migrations. In MongoDB this
change applies the default to the Mongoose model which is useful when
calling payload.db.create() directly.
This only works for statically defined values.
🙏 A big thanks to @r1tsuu for the feature and implementation idea as I
lifted some code from PR https://github.com/payloadcms/payload/pull/6983
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
## Description
### payload
- Removes calls to beginTransaction and commitTransaction from read
operations
### db-sqlite, db-postgres
- beginTransaction() options are passed through and used to create a
transaction
- declare module type adds beginTransaction with proper transaction
config args for postgres and sqlite
Closes#7188
In the collection list view, after adding a filter, the page number
should be reset since the doc count will have changed.
---------
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
Fixes the types for validate functions so that internal validation
functions can be re-used
Currently this has a type error
```ts
validate: (value, args) => {
return text(value, args)
},
```
Fixes#7402
This fixes a regression from changes to the postgres migration template
that were incorrect. It also fixes other type errors for
`payload.db.drizzle` which needed to be declared for postgres to avoid
confusing it with Libsql for SQLite.
## Description
Currently, there is no way to read field props from within a custom
field component, i.e. `admin.components.Description`. For example, if
you set `maxLength: 100` on your field, your custom description
component cannot read it from `props.maxLength` or any other methods.
Because these components are rendered on the server, there is also no
way of using `admin.component.Field` to inject custom props yourself,
either. To support this, we can simply pass the base component props
into these components on the server, as expected. This has also led to
custom field component props becoming more strictly typed within the
config.
This change is considered breaking only because the types have changed.
This only affects you if you were previously importing the following
types into your own custom components. To migrate, simply change the
import paths for that type.
Old:
```ts
import type {
ArrayFieldProps,
ReducedBlock,
BlocksFieldProps,
CheckboxFieldProps,
CodeFieldProps,
CollapsibleFieldProps,
DateFieldProps,
EmailFieldProps,
GroupFieldProps,
HiddenFieldProps,
JSONFieldProps,
NumberFieldProps,
PointFieldProps,
RadioFieldProps,
RelationshipFieldProps,
RichTextComponentProps,
RowFieldProps,
SelectFieldProps,
TabsFieldProps,
TextFieldProps,
TextareaFieldProps,
UploadFieldProps,
ErrorProps,
FormFieldBase,
FieldComponentProps,
FieldMap,
MappedField,
MappedTab,
ReducedBlock,
} from '@payloadcms/ui'
```
New:
```ts
import type {
FormFieldBase,
// etc.
} from 'payload'
```
Custom field components are now much more strongly typed. To make this
happen, an explicit type for every custom component has been generated
for every field type. The convention is to append
`DescriptionComponent`, `LabelComponent`, and `ErrorComponent` onto the
end of the field name, i.e. `TextFieldDescriptionComponent`. Here's an
example:
```ts
import type { TextFieldDescriptionComponent } from 'payload'
import React from 'react'
export const CustomDescription: TextFieldDescriptionComponent = (props) => {
return (
<div id="custom-field-description">{`The max length of this field is: ${props?.maxLength}`}</div>
)
}
```
Here's the full list of all new types:
Label Components:
```ts
import type {
ArrayFieldLabelComponent,
BlocksFieldLabelComponent,
CheckboxFieldLabelComponent,
CodeFieldLabelComponent,
CollapsibleFieldLabelComponent,
DateFieldLabelComponent,
EmailFieldLabelComponent,
GroupFieldLabelComponent,
HiddenFieldLabelComponent,
JSONFieldLabelComponent,
NumberFieldLabelComponent,
PointFieldLabelComponent,
RadioFieldLabelComponent,
RelationshipFieldLabelComponent,
RichTextFieldLabelComponent,
RowFieldLabelComponent,
SelectFieldLabelComponent,
TabsFieldLabelComponent,
TextFieldLabelComponent,
TextareaFieldLabelComponent,
UploadFieldLabelComponent
} from 'payload'
```
Error Components:
```tsx
import type {
ArrayFieldErrorComponent,
BlocksFieldErrorComponent,
CheckboxFieldErrorComponent,
CodeFieldErrorComponent,
CollapsibleFieldErrorComponent,
DateFieldErrorComponent,
EmailFieldErrorComponent,
GroupFieldErrorComponent,
HiddenFieldErrorComponent,
JSONFieldErrorComponent,
NumberFieldErrorComponent,
PointFieldErrorComponent,
RadioFieldErrorComponent,
RelationshipFieldErrorComponent,
RichTextFieldErrorComponent,
RowFieldErrorComponent,
SelectFieldErrorComponent,
TabsFieldErrorComponent,
TextFieldErrorComponent,
TextareaFieldErrorComponent,
UploadFieldErrorComponent
} from 'payload'
```
Description Components:
```tsx
import type {
ArrayFieldDescriptionComponent,
BlocksFieldDescriptionComponent,
CheckboxFieldDescriptionComponent,
CodeFieldDescriptionComponent,
CollapsibleFieldDescriptionComponent,
DateFieldDescriptionComponent,
EmailFieldDescriptionComponent,
GroupFieldDescriptionComponent,
HiddenFieldDescriptionComponent,
JSONFieldDescriptionComponent,
NumberFieldDescriptionComponent,
PointFieldDescriptionComponent,
RadioFieldDescriptionComponent,
RelationshipFieldDescriptionComponent,
RichTextFieldDescriptionComponent,
RowFieldDescriptionComponent,
SelectFieldDescriptionComponent,
TabsFieldDescriptionComponent,
TextFieldDescriptionComponent,
TextareaFieldDescriptionComponent,
UploadFieldDescriptionComponent
} from 'payload'
```
This PR also:
- Standardizes the `FieldBase['label']` type with a new `LabelStatic`
type. This makes type usage much more consistent across components.
- Simplifies some of the typings in the field component map, removes
unneeded `<Omit>`, etc.
- Fixes misc. linting issues around voiding promises
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
So `_Upload` becomes `UploadComponent` which doesnt break the naming
convention of react components and **we no longer export these internal
components**
## Description
Swallows `.abort()` call signal errors
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
Support new `next.config.ts` config file.
Had to do some weird gymnastics around `swc` in order to use it within
unit tests. Had to pass through the `parsed.span.end` value of any
previous iteration and account for it.
Looks to be an open issue here:
https://github.com/swc-project/swc/issues/1366Fixes#7318
## Description
Fixes uploads `filterOptions` not being respected in the Payload admin
UI.
Needs a test written, fixes to types in build, as well as any tests that
fail due to this change in CI.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## 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
## Description
V2 PR [here](https://github.com/payloadcms/payload/pull/7359)
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update
## Checklist:
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
Fixes https://github.com/payloadcms/payload/issues/7341
req.locale was incorrectly set, stemming from initPage, where
req.query.locale was not being used if present inside the
`createLocaleReq` function.
Fixes https://github.com/payloadcms/payload/issues/7271
When extracting the value from the querystring, it is _always_ a string.
We were using a strict equality check which would cause the filter
options to never find the correct option. This caused an infinite loop
when using PG as ID's are numbers by default.
## Description
Fixes#6951
`Feat`: Adds new prop `withMetadata` to `uploads` config that allows the
user to allow media metadata to be appended to the file of the output
media.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
- Abstract shared sql code to a new drizzle package
- Adds sqlite package, not ready to publish until drizzle patches some
issues
- Add `transactionOptions` to allow customizing or disabling db
transactions
- Adds "experimental" label to the `schemaName` property until drizzle
patches an issue
## Description
The first version document throws an error because `latestPublished` and
`latestDraft` are undefined.
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [X] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [X] Existing test suite passes locally with my changes
## Description
V2 PR [here](https://github.com/payloadcms/payload/pull/6923)
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
Adjust logic for determining package manager. Needed to move command
exists logic to be evaluated only after other possibilities were
exhausted.
Closes#7290
Doesn't look like those hacky esm-cjs imports are needed anymore.
These major pino releases only drop Node.js version support for versions
which payload doesn't support anyways.
We do not really need runtime joi schema validation - this is what TypeScript is for. If people are ignoring TypeScript errors in your schema, or JavaScript errors, that is their fault and does not warrant an extra dependency (joi), lots of code to maintain, as well as slower startups.
If we wanna keep runtime schema validation, we should switch to zod so that we can generate TypeScript types based on the schema and do not have to manually maintain config properties in 2 different places (types & schema).
**joi PROs:**
- Safety for JavaScript-only evangelists messing up their schema
- Safety for people putting @ts-expect-error or `as any` everywhere in their code
**joi CONs:**
- Larger bundle size
- More Modules
- Slower Compilation Speed in dev. Worse DX
- Slower Startup (it needs to validate) in dev. Worse DX
- More code to maintain. For every schema change we'll have to change the types AND the joi schema
- TypeScript already throws proper errors if you mess up your schema. Why have runtime errors?
- The errors are bad. They might tell you what field has an issue, but they do not tell you what exactly is wrong. You have probably seen those "Field XY, value is incorrect" errors - and value could mean anything. Worse DX
- Having extra properties in your schema, even if they are useless, doesn't cause any harm
Cons outweigh the pros
**BREAKING:**
- The `deepMerge` exported from payload now handles more complex data and
is slower. The old, simple deepMerge is now exported as `deepMergeSimple`
- `combineMerge` is no longer exported. You can use
`deepMergeWithCombinedArrays` instead
- The behavior of the exported `deepCopyObject` and `isPlainObject` may
be different and more reliable, as the underlying algorithm has changed
Fixes#7101Fixes#7006
Drawers were sending duplicate query params. This new approach modeled after the fix in V2, ensures that each drawer has its own action url created per document and the query params will be created when that is generated.
Also fixes the following:
- incorrect focal point cropping
- generated filenames for animated image names used incorrect heights
Opts to use links instead of router.replace when switching locales. The
main benefit is now the user will be warned if they have changes and
want to switch locales. Before it would switch locales and they would
lose any unsaved changes in the locale they came from.
`auth.loginWithUsername`:
```ts
auth: {
loginWithUsername: {
allowEmailLogin: true, // default: false
requireEmail: false, // default: false
}
}
```
#### `allowEmailLogin`
This property will allow you to determine if users should be able to
login with either email or username. If set to `false`, the default
value, then users will only be able to login with usernames when using
the `loginWithUsername` property.
#### `requireEmail`
Require that users also provide emails when using usernames.
Makes it so generated types now includes a `db` object with `idType` set
to `string` or `number` depending on the database
```ts
db: {
defaultIDType: number;
};
```
The conf dependency being bundled (not even executed) causes frequent
HMR runs (around 10+) to throw multiple MaxListenersExceeded warnings in
the console.
This PR
- fixes telemetry which was previously broken (threw an error which we
ignored) due to a conf version upgrade
- Removes the conf dependency (which is large and comes with a lot of
unneeded dependencies from functionality we don't need, like dot
notation or ajv validation). The important parts of the source code were
copied over - it's now dependency-free
- makes sure we only register the Next.js HMR websocket listener once,
by adding it to the cache
Before this PR:

After this PR:

Canary: 3.0.0-canary.ca3dd1c
- use react 19 types
- no need for dotenv - next has their own dotenv file loader
- disable deprecation warnings by default (newer node version spam you
with it)
- disable turbo by default as hmr is broken and we cannot test against
it yet
- remove ts-node mention in tsconfig as it's not used anymore
- remove unused packages
- [fix: potential seed issues due to parallel payload operations being
on the same
transaction](f899f6a408)
and
b3b565dd75
@DanRibbens can you sense-check this? I do remember that anything
running in parallel should never be on the same transaction
---------
Co-authored-by: Paul Popus <paul@nouance.io>
Something like this:
```ts
{
name: 'select',
type: 'select',
dbName: ({ tableName }) => `${tableName}_customSelect`,
enumName: 'selectEnum',
hasMany: true,
options: ['a', 'b', 'c'],
},
```
caused the "Functions cannot be passed directly to Client Components"
error, as the dbName function was sent to the client.
Now, you can run `pnpm dev database` again without it erroring
We are suspecting that operations within those esbuild scripts are not
awaited properly - potentially causing issues in the publish script,
publishing the next package without any built .js files
## Description
Currently, the Payload doesn't support to extend the Allowed Headers in
CORS context. With this PR, `cors` property can be an object with
`origins` and `headers`.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [ ] Chore (non-breaking change which does not add functionality)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [x] This change requires a documentation update
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
Co-authored-by: Alessio Gravili <alessio@gravili.de>
Fixes#6789
The skipVerify field in NodemailerAdapterArgs worked in reverse of what
it was supposed to do:
- With skipVerify = true -> Verified transport
- With skipVerify = false -> Did not verify transport
This PR makes the property work in the intended way:
- With skipVerify = true -> DO NOT verify transport
- With skipVerify = false -> DO verify transport
We now validate the names of the field against an array of protected
field names.
Also added JSDoc since we can't enforce type strictness yet if `string |
const[]` as it always evaluates to `string`.
```
The name of the field. Must be alphanumeric and cannot contain ' . '
Must not be one of protected field names: ['__v', 'salt', 'hash', 'file']
@link — [https://payloadcms.com/docs/fields/overview#field-names](vscode-file://vscode-app/usr/share/code/resources/app/out/vs/code/electron-sandbox/workbench/workbench.html)
```
- Improves color contrast of various components in the admin panel.
- Adjusts placement of field error tooltips for consistency.
- Corrects misaligned modals.
- Fixes issue where `admin.layout: vertical` was not being applied to
`radio` fields.
## Description
Improves the status pill in the version archive and version comparison
views.
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [X] New feature (non-breaking change which adds functionality)
## Checklist:
- [X] Existing test suite passes locally with my changes
**BREAKING:**
- Upgrades minimum supported @types/react version from
npm:types-react@19.0.0-beta.2 to npm:types-react@19.0.0-rc.0
- Upgrades minimum supported @types/react-dom version from
npm:types-react-dom@19.0.0-beta.2 to npm:types-react-dom@19.0.0-rc.0
- Upgrades minimum supported react and react-dom version from
19.0.0-rc-f994737d14-20240522 to 19.0.0-rc-6230622a1a-20240610
searchPlugin's searchOverrides for the collection now takes in a fields
function instead of array similar to other plugins and patterns we use
to allow you to map over existing fields as well if needed.
```ts
// before
searchPlugin({
searchOverrides: {
slug: 'search-results',
fields: [
{
name: 'excerpt',
type: 'textarea',
admin: {
position: 'sidebar',
},
},
]
},
}),
// current
searchPlugin({
searchOverrides: {
slug: 'search-results',
fields: ({ defaultFields }) => [
...defaultFields,
{
name: 'excerpt',
type: 'textarea',
admin: {
position: 'sidebar',
},
},
]
},
}),
```
- Upgrades eslint from v8 to v9
- Upgrades all other eslint packages. We will have to do a new
full-project lint, as new rules have been added
- Upgrades husky from v8 to v9
- Upgrades lint-staged from v14 to v15
- Moves the old .eslintrc.cjs file format to the new eslint.config.js
flat file format.
Previously, we were very specific regarding which rules are applied to
which files. Now that `extends` is no longer a thing, I have to use
deepMerge & imports instead.
This is rather uncommon and is not a documented pattern - e.g.
typescript-eslint docs want us to add the default typescript-eslint
rules to the top-level & then disable it in files using the
disable-typechecked config.
However, I hate this opt-out approach. The way I did it here adds a lot
of clarity as to which rules are applied to which files, and is pretty
easy to read. Much less black magic
## .eslintignore
These files are no longer supported (see
https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files).
I moved the entries to the ignores property in the eslint config. => one
less file in each package folder!
## Description
Exports `getSiblingData`, `getDataByPath`, `reduceFieldsToValues`, and
`unflatten` from `payload`. These utilities were previously accessible
using direct import paths from `@payloadcms/ui`—but this is no longer
advised since moving to a pre-bundled UI library pattern. Instead of
simply exporting these from the `@payloadcms/ui` package, these exports
have been moved to Payload itself to provision for use outside of React
environments.
This is considered a breaking change. If you were previously importing
any of these utilities, the imports paths have changed as follows:
Old:
```ts
import { getSiblingData, getDataByPath, reduceFieldsToValues } from '@payloadcms/ui/forms/Form'
import { unflatten } from '@payloadcms/ui/utilities'
```
New:
```ts
import { getSiblingData, getDataByPath, reduceFieldsToValues, unflatten } from 'payload/shared'
```
The `is-buffer` dependency was also removed in this PR.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
## Description
This is the beta (v3) PR for the v2 PR
[here](https://github.com/payloadcms/payload/pull/6857)
Addresses #6800, #5108
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
## Description
BREAKING CHANGE: Color values have changed and will have different
contrasts. If you use any of Payload's colors in your apps, you may need
to adjust your use of them to maintain proper styling/accessibility.
Colors palettes changed:
- `--theme-success-*`
- `--theme-error-*`
- `--theme-warning-*`
- `--color-success-*`
- `--color-error-*`
- `--color-warning-*`
- `--color-blue-*`
Updates the color palette used throughout Payload to be more consistent
between dark and light values. Contrast values are now more in line with
the `theme-elevation` contrasts. Some adjustments to the Toast
components as well to match light/dark mode better.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [x] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [x] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core 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
**BREAKING:** The minimum required Next.js version has been bumped from
`15.0.0-rc.0` to `15.0.0-canary.53`. This is because the way client
components are represented changed somewhere between those versions, and
it is not feasible to support both versions in our RSC detection logic.
## Description
Adds the ability to filter by fields within a `group` or **named** `tab`
via the list controls.
Note: added missing translations for the `within` and `intersects`
operator options, these are displayed in the filters for `point` and
`JSON` fields.
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [X] New feature (non-breaking change which adds functionality)
## Checklist:
- [X] Existing test suite passes locally with my changes
- Improves type for `jsonSchema` property of JSON field
- Adds type generation of JSON field with `jsonSchema`
- Adds `typescriptSchema` property to fields that allows you override
default field type generation by providing a JSON schema.
- Adds `typescript.schema` property in payload config, to allow for any
modifications of the type schemas
---------
Co-authored-by: Alessio Gravili <alessio@gravili.de>
## Description
Adds `loginWithUsername` option to auth config. When set to true, it
will inject an `username` field into the collection config which
replaces the `email` field in the UI. The `email` field is still
required but not unique.
The `username` field can be extended by passing a field named `username`
to your auth collection. Anything added to this field will be combined
with the initial field.
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [X] New feature (non-breaking change which adds functionality)
## Checklist:
- [X] Existing test suite passes locally with my changes
---------
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
Exports the fields from the SEO plugin so that they can be used anywhere
inside a collection, new exports:
```ts
import { MetaDescriptionField, MetaImageField, MetaTitleField, OverviewField, PreviewField } from '@payloadcms/plugin-seo/fields'
// Used as fields
MetaImageField({
relationTo: 'media',
hasGenerateFn: true,
})
MetaDescriptionField({
hasGenerateFn: true,
})
MetaTitleField({
hasGenerateFn: true,
})
PreviewField({
hasGenerateFn: true,
titlePath: 'meta.title',
descriptionPath: 'meta.description',
})
OverviewField({
titlePath: 'meta.title',
descriptionPath: 'meta.description',
imagePath: 'meta.image',
})
```
Removes PayloadRequestWithData in favour of just PayloadRequest with
optional types for `data` and `locale`
`addDataAndFileToRequest` and `addLocalesToRequestFromData` now takes in
a single argument instead of an object
```ts
// before
await addDataAndFileToRequest({ request: req })
addLocalesToRequestFromData({ request: req })
// current
await addDataAndFileToRequest(req)
addLocalesToRequestFromData(req)
```
---------
Co-authored-by: Paul Popus <paul@nouance.io>
Closes https://github.com/payloadcms/payload/issues/6953
```ts
// the following types can now take in arguments for User type
GenerateVerifyEmailHTML<User>
GenerateVerifyEmailSubject<User>
GenerateForgotPasswordEmailHTML<User>
GenerateForgotPasswordEmailSubject<User>
```
BREAKING: `ValidationError` now requires the `global` or `collection`
slug, as well as an `errors` property. The actual errors are no longer
at the top-level.
Changed the data to correctly match type generic being sent to the
generate functions. So now you can type your generateTitle etc.
functions like this
```ts
// before
const generateTitle: GenerateTitle = async <Page>({ doc, locale }) => {
return `Website.com — ${doc?.title?.value}`
}
// curent
import type { GenerateDescription, GenerateTitle, GenerateURL } from '@payloadcms/plugin-seo/types'
import type { Page } from './payload-types'
const generateTitle: GenerateTitle<Page> = async ({ doc, locale }) => {
return `Website.com — ${doc?.title}`
}
const generateDescription: GenerateDescription<Page> = async ({ doc, locale }) => {
return doc?.excerpt || 'generated description'
}
const generateURL: GenerateURL<Page> = async ({ doc, locale }) => {
return `https://yoursite.com/${locale ? locale + '/' : ''}${doc?.slug || ''}`
}
```
Breaking change because it was previously a FormState value.
## Description
Some authentication strategies may need to set headers for responses,
such as updating cookies via a refresh token, and similar. This PR
extends Payload's auth strategy capabilities with a manner of
accomplishing this.
This is a breaking change if you have custom authentication strategies
in Payload's 3.0 beta. But it's a simple one to update.
Instead of your custom auth strategy returning the `user`, now you must
return an object with a `user` property.
This is because you can now also optionally return `responseHeaders`,
which will be returned by Payload API responses if you define them in
your auth strategies. This can be helpful for cases where you need to
set cookies and similar, directly within your auth strategies.
Before:
```ts
return user
```
After:
```ts
return { user }
```
## Description
<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->
The v3 documentation mislead people by using PassportJS even though it's
not in v3 and custom strategies should be used instead with the correct
link.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Chore (non-breaking change which does not add functionality)
- [x] This change requires a documentation update
## Checklist:
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
In case of breaking lexical data changes, you can simply call
`upgradeLexicalData({ payload })` to upgrade every lexical field in your
payload field to the new data format.
## Description
Properties within the Custom Collection Components config were not
properly cased. In the Payload Config, there are places where we expose
_an array_ of Custom Components to render. These properties should be
cased in `camelCase` to indicate that its type is _**not**_ a component,
but rather, it's an _**array**_ of components. This is how all other
arrays are already cased throughout the config, therefore these
components break exiting convention. The `CapitalCase` convention is
reserved for _components themselves_, however, fixing this introduces a
breaking change. Here's how to migrate:
Old:
```ts
{
// ...
admin: {
components: {
AfterList: [],
AfterListTable: [],
BeforeList: [],
BeforeListTable: [],
}
}
}
```
New:
```ts
{
// ...
admin: {
components: {
afterList: [],
afterListTable: [],
beforeList: [],
beforeListTable: [],
}
}
}
```
The docs were also out of date for the Root-level Custom Components.
These components are documented in CaptalCase but are in fact cased
correctly in Payload. This PR fixes that.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] This change requires a documentation update
## Checklist:
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
## Description
Ensures that exp and auth strategy are available from the `me` and
`refresh` operations as well as passed through the `Auth` provider. Same
as #6943
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
Adds the ability to set response headers by using a new
`uploads.modifyResponseHeaders` property. You could previously do this
in Express in Payload v2.
You can do this like so:
```ts
upload: {
modifyResponseHeaders: ({ headers }) => {
headers.set('Cache-Control', 'public, max-age=86400')
return headers
}
},
```
**BREAKING:**
- Type narrowing for `relationTo` props on filterOptions, relationship
fields and upload fields
- Type narrowing for arguments of lexical relationship, link and upload
features
## Description
Standardizes all named field exports. This improves semantics when using
these components by appending `Field` onto the end of their names. Some
components were already doing this, i.e. `ArrayField` and `BlocksField`.
Now, all field components share this same convention. And since bundled
components were already aliasing most exports in this way, this change
will largely go unnoticed because most apps were _already_ importing the
correctly named components. What is ultimately means is that there was a
mismatch between the unbundled vs bundled exports. This PR resolves that
conflict. But this also introduces a potentially breaking change for
your app. If your app is using components that import from the
_unbundled_ `@payloadcms/ui` package, those import paths likely changed:
Old:
```tsx
import { Text } from '@payloadcms/ui/fields/Text'
```
New:
```tsx
import { TextField } from '@payloadcms/ui/fields/Text'
```
If you were importing direcetly from the _bundled_ version, you're
imports likely have not changed. For example:
This still works (the import path is top-level, pointing to the
_bundled_ code):
```tsx
import { TextField } from '@payloadcms/ui'
```
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
## Checklist:
- [x] Existing test suite passes locally with my changes
**BREAKING:** a bunch of exports have been moved around. There are now
two of them: `@payloadcms/richtext-lexical` and
`@payloadcms/richtext-lexical/client`. The root export is server-only.
If any imports don't resolve anymore after this version, simply change
the import to one of those, depending on if you are on the server or the
client
**BREAKING:**
- ServerFeature: `ClientComponent` has been renamed to `ClientFeature`
- ServerFeature: The nested `serverFeatureProps` has been renamed to
`sanitizedServerFeatureProps`
- ServerFeature: The FeatureProviderProviderServer type now expects 3
generics instead of 2. We have split the props generic into sanitized &
unsanitized props
- ClientFeature: The FeatureProviderProviderClient type now expects 2
generics instead of 1. We have split the props generic into sanitized &
unsanitized props
- ClientFeature: The nested `clientFeatureProps` has been renamed to
`sanitizedClientFeatureProps`
Fixes https://github.com/payloadcms/payload/issues/6869
Before, options from props were being stored in state and would not
update when props changed. Now options are memoized and will update when
the incoming `options` prop changes.
Allows `upload.handlers` to mutate the request. This can be useful when
you want to adjust headers on the request but do not want to return a
new response.
**BREAKING:** All `@payloadcms/ui/client` exports have been renamed to
`@payloadcms/ui`. A simple find & replace across your entire project
will be enough to migrate. This change greatly improves import
auto-completions in IDEs which lack proper support for package.json
exports, like Webstorm.
Copy of https://github.com/payloadcms/payload/pull/6842 for beta
Allows empty strings ('') as defaultValue for fields of types: 'text'; 'textarea'; 'email'; 'code'. This can be useful when you want to ensure the value is always a string instead of null/undefined.
## Description
Fixes an issue where the `unflatten` function would also unflatten json
objects when they contained a `.` in one of their keys
V2 PR [here](https://github.com/payloadcms/payload/pull/6834)
## Description
Fixes an issue where if you define a `basePath` in your `next` config,
the logout button would redirect you to `/admin/logout` instead of
`/basePath/admin/logout` causing a 404.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
# Breaking Changes
### New file import locations
Exports from the `payload` package have been _significantly_ cleaned up.
Now, just about everything is able to be imported from `payload`
directly, rather than an assortment of subpath exports. This means that
things like `import { buildConfig } from 'payload/config'` are now just
imported via `import { buildConfig } from 'payload'`. The mental model
is significantly simpler for developers, but you might need to update
some of your imports.
Payload now exposes only three exports:
1. `payload` - all types and server-only Payload code
2. `payload/shared` - utilities that can be used in either the browser
or in Node environments
3. `payload/node` - heavy utilities that should only be imported in Node
scripts and never be imported into bundled code like Next.js
### UI library pre-bundling
With this release, we've dramatically sped up the compile time for
Payload by pre-bundling our entire UI package for use inside of the
Payload admin itself. There are new exports that should be used within
Payload custom components:
1. `@payloadcms/ui/client` - all client components
2. `@payloadcms/ui/server` - all server components
For all of your custom Payload admin UI components, you should be
importing from one of these two pre-compiled barrel files rather than
importing from the more deeply nested exports directly. That will keep
compile times nice and speedy, and will also make sure that the bundled
JS for your admin UI is kept small.
For example, whereas before, if you imported the Payload `Button`, you
would have imported it like this:
```ts
import { Button } from '@payloadcms/ui/elements/Button'
```
Now, you would import it like this:
```ts
import { Button } from '@payloadcms/ui/client'
```
This is a significant DX / performance optimization that we're pretty
pumped about.
However, if you are importing or re-using Payload UI components
_outside_ of the Payload admin UI, for example in your own frontend
apps, you can import from the individual component exports which will
make sure that the bundled JS is kept to a minimum in your frontend
apps. So in your own frontend, you can continue to import directly to
the components that you want to consume rather than importing from the
pre-compiled barrel files.
Individual component exports will now come with their corresponding CSS
and everything will work perfectly as-expected.
### Specific exports have changed
- `'@payloadcms/ui/templates/Default'` and
`'@payloadcms/ui/templates/Minimal`' are now exported from
`'@payloadcms/next/templates'`
- Old: `import { LogOut } from '@payloadcms/ui/icons/LogOut'` new:
`import { LogOutIcon } from '@payloadcms/ui/icons/LogOut'`
## Background info
In effort to make local dev as fast as possible, we need to import as
few files as possible so that the compiler has less to process. One way
we've achieved this in the Admin Panel was to _remove_ all .scss imports
from all components in the `@payloadcms/ui` module using a build
process. This stripped all `import './index.scss'` statements out of
each component before injecting them into `dist`. Instead, it bundles
all of the CSS into a single `main.css` file, and we import _that_ at
the root of the app.
While this concept is _still_ the right solution to the problem, this
particular approach is not viable when using these components outside
the Admin Panel, where not only does this root stylesheet not exist, but
where it would also bloat your app with unused styles. Instead, we need
to _keep_ these .scss imports in place so they are imported directly
alongside your components, as expected. Then, we need create a _new_
build step that _separately_ compiles the components _without_ their
stylesheets—this way your app can consume either as needed from the new
`client` and `server` barrel files within `@payloadcms/ui`, i.e. from
within `@payloadcms/next` and all other admin-specific packages and
plugins.
This way, all other applications will simply import using the direct
file paths, just as they did before. Except now they come with
stylesheets.
And we've gotten a pretty awesome initial compilation performance boost.
---------
Co-authored-by: James <james@trbl.design>
Co-authored-by: Alessio Gravili <alessio@gravili.de>
Fixes https://github.com/payloadcms/payload/issues/6745
Fixes the inability to navigate to the reset password route. Adds the ability to customize the route and docs for all customizable admin panel routes.
Fixes: https://github.com/payloadcms/payload/issues/6486
Adds `X-HTTP-Method-Override` header to allow for sending query params in the body of a POST request. This is useful when the query param string hits the upper limit.
## Description
V2 PR [here](https://github.com/payloadcms/payload/pull/6733)
Additionally fixes issue with image thumbnails not updating properly
until page refresh.
Image thumbnails properly update on document save now.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
## BREAKING
- Our internal field hook methods now have new required `schemaPath` and
path `props`. This affects the following functions, if you are using
those: `afterChangeTraverseFields`, `afterReadTraverseFields`,
`beforeChangeTraverseFields`, `beforeValidateTraverseFields`,
`afterReadPromise`
- The afterChange field hook's `value` is now the value AFTER the
previous hooks were run. Previously, this was the original value, which
I believe is a bug
- Only relevant if you have built your own richText adapter: the
richText adapter `populationPromises` property has been renamed to
`graphQLPopulationPromises` and is now only run for graphQL. Previously,
it was run for graphQL AND the rest API. To migrate, use
`hooks.afterRead` to run population for the rest API
- Only relevant if you have built your own lexical features: The
`populationPromises` server feature property has been renamed to
`graphQLPopulationPromises` and is now only run for graphQL. Previously,
it was run for graphQL AND the rest API. To migrate, use
`hooks.afterRead` to run population for the rest API
- Serialized lexical link and upload nodes now have a new `id` property.
While not breaking, localization / hooks will not work for their fields
until you have migrated to that. Re-saving the old document on the new
version will automatically add the `id` property for you. You will also
get a bunch of console logs for every lexical node which is not migrated
**BREAKING:** We now export toast from `sonner` instead of
`react-toastify`. If you send out toasts from your own projects, make
sure to use our `toast` export, or install `sonner`. React-toastify
toasts will no longer work anymore. The Toast APIs are mostly similar,
but there are some differences if you provide options to your toast
CSS styles have been changed from Toastify
```css
/* before */
.Toastify
/* current */
.payload-toast-container
.payload-toast-item
.payload-toast-close-button
/* individual toast items will also have these classes depending on the state */
.toast-info
.toast-warning
.toast-success
.toast-error
```
https://github.com/payloadcms/payload/assets/70709113/da3e732e-aafc-4008-9469-b10f4eb06b35
---------
Co-authored-by: Paul Popus <paul@nouance.io>
## Description
### Issue:
Non-animated webp / gif files were using `metadata.pages` to calculate
it's resized heights for `imageSizes` or `cropping`.
### Fix:
It should only use this to calculate it's height if the file's
`metadata` contains `metadata.pages`. Non-animated webps and gifs would
not have this.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
## Description
Fixes#6694
Previously we were only creating sharp files for files that have file
adjustments but instead a sharp file should be created for animated
images even if there are no file adjustments - i.e
`const fileHasAdjustments = fileSupportsResize && Boolean(resizeOptions
|| formatOptions || imageSizes || trimOptions || file.tempFilePath)`
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
Fixes https://github.com/payloadcms/payload/issues/6637
There was an issue where tab paths were being generated based on 2
scenarios when there are 3 possible scenarios:
- A path is provided and the tab is named
- A path is **not** provided but the tab is named
- Neither a path or a tab name are provided
Types are now auto-generated by default.
You can opt-out of this behavior by setting:
```ts
buildConfig({
// Rest of config
typescript: {
autoGenerate: false
},
})
```
## Description
Allows draft validation to be enabled at the config level.
You can enable this by:
```ts
// ...collectionConfig
versions: {
drafts: {
validate: true // defaults to false
}
}
```
## Description
Updates the `fields` override in plugin redirects to allow for
overriding
```ts
// before
overrides: {
fields: [
{
type: 'text',
name: 'customField',
},
],
},
// current
overrides: {
fields: ({ defaultFields }) => {
return [
...defaultFields,
{
type: 'text',
name: 'customField',
},
]
},
},
```
## Type of change
- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
**BREAKING**:
- This bumps the minimum required node version from node 20.6.0 to node
20.9.0. This is because 20.6.0 breaks type generation due to a CJS node
bug, and 20.9.0 is the next v20 LTS version. The minimum node 18 version
stays the same (18.20.2)
## Description
fixes#6630
# BREAKING CHANGES
This only applies to you if you using db-postgres and have created the
`v2-v3-relationships` migration released in
[v3.0.0-beta.39](https://github.com/payloadcms/payload/releases/tag/v3.0.0-beta.39)
from @payloadcms/db-postgres <= v3.0.0-beta.40.
### Steps to fix
- Delete the existing v2-v3-relationships migration file.
- If changes were made to your config since the previous migration was
made, you will need to revert those by checking out a previous commit in
your version control.
- Recreate the migration using `payload migrate:create --file
@payloadcms/db-postgres/relationships-v2-v3` to make the migration with
the snapshot .json file.
**BREAKING:**
- This upgrades the required version of lexical from 0.15.0 to 0.16.0.
If you are using lexical directly in your project, possibly due to
custom features, there might be breaking changes for you. Please consult
the lexical 0.16.0 changelog:
https://github.com/facebook/lexical/releases/tag/v0.16.0
**BREAKING**: useEditorFocusProvider has been removed and merged with
useEditorConfigContext. You can now find information about the focused
editor, parent editors and child editors within useEditorConfigContext
BREAKING CHANGE:
Moves `upload` field and `relationship` fields with `hasMany: false` &
`relationTo: string` from the many-to-many `_rels` join table to simple
columns. This only affects Postgres database users.
## TL;DR
We have dramatically simplified the storage of simple relationships in
relational databases to boost performance and align with more expected
relational paradigms. If you are using the beta Postgres adapter, and
you need to keep simple relationship data, you'll need to run a
migration script that we provide you.
### Background
For example, prior to this update, a collection of "posts" with a simple
`hasMany: false` and `relationTo: 'categories'` field would have a
`posts_rels` table where the category relations would be stored.
This was somewhat unnecessary as simple relations like this can be
expressed with a `category_id` column which is configured as a foreign
key. This also introduced added complexity for dealing directly with the
database if all you have are simple relations.
### Who needs to migrate
You need to migrate if you are using the beta Postgres database adapter
and any of the following applies to you.
- If you have versions enabled on any collection / global
- If you use the `upload` field
- If you have relationship fields that are `hasMany: false` (default)
and `relationTo` to a single category ([has
one](https://payloadcms.com/docs/fields/relationship#has-one)) relations
### We have a migration for you
Even though the Postgres adapter is in beta, we've prepared a predefined
migration that will work out of the box for you to migrate from an
earlier version of the adapter to the most recent version easily.
It makes the schema changes in step with actually moving the data from
the old locations to the new before adding any null constraints and
dropping the old columns and tables.
### How to migrate
The steps to preserve your data while making this update are as follows.
These steps are the same whether you are moving from Payload v2 to v3 or
a previous version of v3 beta to the most recent v3 beta.
**Important: during these steps, don't start the dev server unless you
have `push: false` set on your Postgres adapter.**
#### Step 1 - backup
Always back up your database before performing big changes, especially
in production cases.
#### Step 2 - create a pre-update migration
Before updating to new Payload and Postgres adapter versions, run
`payload migrate:create` without any other config changes to have a
prior snapshot of the schema from the previous adapter version
#### Step 3 - if you're migrating a dev DB, delete the dev `push` row
from your `payload_migrations` table
If you're migrating a dev database where you have the default setting to
push database changes directly to your DB, and you need to preserve data
in your development database, then you need to delete a `dev` migration
record from your database.
Connect directly to your database in any tool you'd like and delete the
dev push record from the `payload_migrations` table using the following
SQL statement:
```sql
DELETE FROM payload_migrations where batch = -1`
```
#### Step 4 - update Payload and Postgres versions to most recent
Update packages, making sure you have matching versions across all
`@payloadcms/*` and `payload` packages (including
`@payloadcms/db-postgres`)
#### Step 5 - create the predefined migration
Run the following command to create the predefined migration we've
provided:
```
payload migrate:create --file @payloadcms/db-postgres/relationships-v2-v3
```
#### Step 6 - migrate!
Run migrations with the following command:
```
payload migrate
```
Assuming the migration worked, you can proceed to commit this change and
distribute it to be run on all other environments.
Note that if two servers connect to the same database, only one should
be running migrations to avoid transaction conflicts.
Related discussion:
https://github.com/payloadcms/payload/discussions/4163
---------
Co-authored-by: James <james@trbl.design>
Co-authored-by: PatrikKozak <patrik@payloadcms.com>
## Description
V2 PR [here](https://github.com/payloadcms/payload/pull/6530)
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] This change requires a documentation update
## Checklist:
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
## Description
Changes the `fields` override for form builder plugin to use a function
instead so that we can actually override existing fields which currently
will not work.
```ts
//before
fields: [
{
name: 'custom',
type: 'text',
}
]
// current
fields: ({ defaultFields }) => {
return [
...defaultFields,
{
name: 'custom',
type: 'text',
},
]
}
```
## Type of change
- [x] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
Updates create-payload-app to update an existing payload installation
- Detects existing Payload installation. Fixes#6517
- If not latest, will install latest and grab the `(payload)` directory
structure (ripped from `templates/blank-3.0`
## Description
Fixed missing Hebrew language export in payload/i18n module.
The import statement import { he } from 'payload/i18n/he' was not
functioning due to he not being exported correctly.
<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Chore (non-breaking change which does not add functionality)
Updates the regex to allow relative and anchor links as well. Manually
tested all common variations of absolute, relative and anchor links with
a combination
## Type of change
- [x] New feature (non-breaking change which adds functionality)
Closes#6455. Proper localization support will be worked on later, this
just resolves the issue where having it enabled not only doesn't
localize those fields, it also omits them from the API response. Now,
they are not omitted, and localization is simply skipped.
**BREAKING:**
- bumps minimum required next.js version from `14.3.0-canary.68` to
`15.0.0-rc.0`
- bumps minimum required react and react-dom versions to `19.0.0
`(`19.0.0-rc-f994737d14-20240522` should be used)
- `@types/react` and `@types/react-dom` have to be bumped to
`npm:types-react@19.0.0-beta.2` using overrides and pnpm overrides, if
you want correct types. You can find an example of this here:
https://github.com/payloadcms/payload/pull/6429/files#diff-10cb9e57a77733f174ee2888587281e94c31f79e434aa3f932a8ec72fa7a5121L32
## Issues
- Bunch of todos for our react-select package which is having type
issues. Works fine, just type issues. Their type defs are importing JSX
in a weird way, we likely just have to wait until they fix them in a
future update.
Fixes webpack issue with isHotkey: `TypeError:
is_hotkey__WEBPACK_IMPORTED_MODULE_9__ is not a function`
Changing this from a default import to a named export, and it appears to
resolve the issue.
Fixes#6421
## Description
Renames the `Save` to `SaveButton`, etc. to match the already
established convention of the `PreviewButton`, etc. This matches the
imports with their respective component and type names, and also gives
these components more context to the developer whenever they're
rendered, i.e. its clearly just a button and not an entire block or
complex component.
**BREAKING**:
Import paths for these components have changed, if you were previously
importing these components into your own projects to customize, change
the import paths accordingly:
Old:
```ts
import { PublishButton } from '@payloadcms/ui/elements/Publish'
import { SaveButton } from '@payloadcms/ui/elements/Save'
import { SaveDraftButton } from '@payloadcms/ui/elements/SaveDraft'
```
New:
```ts
import { PublishButton } from '@payloadcms/ui/elements/PublishButton'
import { SaveButton } from '@payloadcms/ui/elements/SaveButton'
import { SaveDraftButton } from '@payloadcms/ui/elements/SaveDraftButton'
```
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Description
Setting `disableListColumn` to `true` on a field would hide the field
from the column selector but not from the table columns.
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
Change the exports of DefaultListView and DefaultEditView to be renamed
without "Default" as ListView
```ts
// before
import { DefaultEditView } from '@payloadcms/next/views'
import { DefaultListView } from '@payloadcms/next/views'
// after
import { EditView } from '@payloadcms/next/views'
import { ListView } from '@payloadcms/next/views'
```
Added additional prompt to make sure the translation we receive is using
formal language where it makes sense.
In the context of latin languages for example:
- Spanish: "tu" should be using "vos"
- French: "tu" should be using "votre"
These differences can affect verb conjugations and in these languages it
comes across as less professional if informal language is used.
## Description
Closes
[#225](https://github.com/payloadcms/payload-3.0-demo/issues/225).
The user verification emails are not being sent and this error is shown:
```ts
APIError: Error sending email: 422 validation_error - Invalid `from` field. The email address needs to follow the `email@example.com` or `Name <email@example.com>` format.
```
The issue is resolved by updating the `from` property on the outgoing
verification email:
```ts
from: `"${email.defaultFromName}" <${email.defaultFromName}>`,
// to
from: `"${email.defaultFromName}" <${email. defaultFromAddress}>`,
```
**NOTE:** This was not broken in 2.0, see correct outgoing email
[here](https://github.com/payloadcms/payload/blob/main/packages/payload/src/auth/sendVerificationEmail.ts#L69).
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [X] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [X] Existing test suite passes locally with my changes
## Description
Fixes https://github.com/payloadcms/payload-3.0-demo/issues/181
Although issue is about page changing, it happens as well when you
change sort / limit / where filter (and probably locale)
<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
<!-- Please delete options that are not relevant. -->
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
---------
Co-authored-by: Jessica Chowdhury <jessica@trbl.design>
## Description
Fixes an issue with creating versions when using custom DB names,
`uuid`, and drafts.
---------
Co-authored-by: PatrikKozak <patrik@payloadcms.com>
When typing into the search input on the list view of a collection, the
`like` operator is used for id which causes an error for postgres. To
fix this we are sanitizing the `like` for number or uuid fields to
instead be an `equals` operator. An alternate solution would have been
to cast the ids to text `id::text` but this would have performence
implications on larger data sets.
---------
Co-authored-by: James <james@trbl.design>
## Description
* The apostrophe character `’` should be used instead of the single
quote `'`
* Gender corrections: "L’adresse e-mail fourni**e**", "Vérification
échoué**e**"
* Lowercase: "Supprimer le **té**léversement"
* Dark and light theme: I think it makes more sense to use "Sombre" and
"Clair" here to identify the theme. Day/Night modes imply a hue/warmth
correction and are different features altogether. Reference:
https://fr.wikipedia.org/wiki/Mode_sombre#Mode_sombre_et_mode_nuit_ou_chaud
* Fix accent: "Mis à jour avec succ**è**s"
* "Bienvenue" I think would be the correct standalone greeting form.
Reference:
https://www.projet-voltaire.fr/question-orthographe/orthographe-bienvenu-bienvenue-chez-moi/
* "Recadrer" is the correct word for "crop". "Récolte" means "crop" in
the sense of "harvest", so this was probably a bad literal Google
Translate that slipped through.
* Correct all "Es-tu sûr ?" to the proper formal "Êtes-vous sûr ?" for
consistency
* Use _article défini_ since we will enumerate the values: "Ce champ
contient **les** sélections invalides suivantes :"
* Space before question marks
---
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
**BREAKING:**
- The minimum required next version is now 14.3.0-canary.68. This is
because we are migrating away from the deprecated
experimental.serverComponentsExternalPackages next config key to
experimental.serverExternalPackages, which is not available in older
next canaries
- The minimum `react` and `react-dom` versions have been bumped to
^18.2.0 or ^19.0.0. This matches the minimum react version recommended
by next
## Description
Issue with editing and changing the crop or focal point of an image
`fix`: adds optional chaining to safely access cookie header when
fetching image
v2 PR [here](https://github.com/payloadcms/payload/pull/6367)
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] Existing test suite passes locally with my changes
## Description
v2 PR [here](https://github.com/payloadcms/payload/pull/6358)
- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
**BREAKING:** This upgrades all lexical packages from 0.14.5 to 0.15.0.
If there are any breaking changes within lexical, this could break your
project if you use lexical APIs directly (e.g. in custom features). We
have not noticed any breaking changes within core. Please consult their
changelog: https://github.com/facebook/lexical/releases/tag/v0.15.0
## Description
Fixes https://github.com/payloadcms/payload-3.0-demo/issues/215
imports like import LogoSVG from '@/components/logo.svg' did not make it
to the TS module resolution, due to the early isClient check.
And the isClient check only uses node module resolution (using
nextResolves) which throws an error here.
This removes module resolution completely, as we "ignore" client files
anyways. Should also help improve performance, and we do not have to
fall back to ts module resolution for client files that way, which would
be unnecessary
## Description
Closes [#117](https://github.com/payloadcms/payload-3.0-demo/issues/177)
- hitting the space key while the `ReactSelect` is in focus crashes the
page.
This PR makes the following changes:
- Multivalue select component updated to only use `id`, drag feature
does not work when using `uuid()`
- Ensures relationship field (multi and single value) can be accessed
via the keyboard
- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
## Type of change
- [X] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [X] Existing test suite passes locally with my changes
**Breaking:** The following, exported components now need the `payload` object as a prop rather than the `config` object:
- `RenderCustomComponent` (optional)
- `Logo`
- `DefaultTemplate`
- `DefaultNav`
Breaking Changes:
- Globals config: `admin.description` no longer accepts a custom component. You will have to move it to `admin.components.elements.Description`
- Collections config: `admin.description` no longer accepts a custom component. You will have to move it to `admin.components.edit.Description`
- All Fields: `field.admin.description` no longer accepts a custom component. You will have to move it to `field.admin.components.Description`
- Collapsible Field: `field.label` no longer accepts a custom component. You will have to move it to `field.admin.components.RowLabel`
- Array Field: `field.admin.components.RowLabel` no longer accepts strings or records
- If you are using our exported field components in your own app, their `labelProps` property has been stripped down and no longer contains the `label` and `required` prop. Those can now only be configured at the top-level
**BREAKING:**
- Narrows the type of the `plugins` prop of lexical features. Client props are now also automatically provided to the plugin components. To migrate, type your plugin as either `PluginComponent` or PluginComponentWithAnchor.
- `BlockQuoteFeature` has been renamed to `BlockquoteFeature`
- `createClientComponent` is now exported only from /components
- The `LexicalBlocks` and `FieldWithRichTextRequiredEditor` types have been removed in favor of just `Blocks` & `Fields`, as well as improved validation.
BREAKING:
- The default inline toolbar has now been extracted into an `InlineToolbarFeature`. While it's part of the defaultFeatures, you might have to add it to your editor features if you are not including the defaultFeatures and still want to keep the inline toolbar (floating toolbar)
- Some types have been renamed, e.g. `InlineToolbarGroup` is now `ToolbarGroup`, and `InlineToolbarGroupItem` is now `ToolbarGroupItem`
- The `displayName` property of SlashMenuGroup and SlashMenuItem has been renamed to `label` to match the `label` prop of the toolbars
- The `inlineToolbarFeatureButtonsGroupWithItem`, `inlineToolbarFormatGroupWithItems` and `inlineToolbarTextDropdownGroupWithItems` exports have been renamed to `toolbarTextDropdownGroupWithItems`, `toolbarFormatGroupWithItems`, `toolbarFeatureButtonsGroupWithItems`
BREAKING CHANGE: All plugins have been updated to use named exports and the names have been updated to be consistent.
// before
import { cloudStorage } from '@payloadcms/plugin-cloud-storage'
// current
import { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'
//before
import { payloadCloud } from '@payloadcms/plugin-cloud'
// current
import { payloadCloudPlugin } from '@payloadcms/plugin-cloud'
//before
import formBuilder from '@payloadcms/plugin-form-builder'
// current
import { formBuilderPlugin } from '@payloadcms/plugin-form-builder'
//before
import { nestedDocs } from '@payloadcms/plugin-nested-docs'
// current
import { nestedDocsPlugin } from '@payloadcms/plugin-nested-docs'
//before
import { redirects } from '@payloadcms/plugin-redirects'
// current
import { redirectsPlugin } from '@payloadcms/plugin-redirects'
// before
import search from '@payloadcms/plugin-search'
// current
import { searchPlugin } from '@payloadcms/plugin-search'
//before
import { sentry } from '@payloadcms/plugin-sentry'
// current
import { sentryPlugin } from '@payloadcms/plugin-sentry'
// before
import { seo } from '@payloadcms/plugin-seo'
// current
import { seoPlugin } from '@payloadcms/plugin-seo'
**BREAKING:**
If you have own, custom lexical features, there will be a bunch of breaking API changes for you. The saved JSON data is not affected.
- `floatingSelectToolbar` has been changed to `toolbarInline`
- `slashMenu.dynamicOptions `and `slashMenu.options` have been changed to `slashMenu.groups` and `slashMenu.dynamicGroups`
- `toolbarFixed.sections` is now `toolbarFixed.groups`
- Slash menu group `options` and toolbar group `entries` have both been renamed to `items`
- Toolbar group item `onClick` has been renamed to `onSelect` to match slash menu properties
- slashMenu item `onSelect` is no longer auto-wrapped inside an `editor.update`. If you perform editor updates in them, you have to wrap it inside an `editor.update` callback yourself. Within our own features this extra control has removed a good amount of unnecessary, nested `editor.update` calls, which is good
- Slash menu items are no longer initialized using the `new` keyword, as they are now types and no longer classes. You can convert them to an object and add the `key` property as an object property instead of an argument to the previous SlashMenuItem constructor
- CSS classnames for slash menu and toolbars, as well as their items, have changed
- `CheckListFeature` is now exported as and has been renamed to `ChecklistFeature`
For guidance on migration, check out how we migrated our own features in this PR's diff: https://github.com/payloadcms/payload/pull/6191/files
BREAKING: this will now display errors if you're previously had invalid link or upload fields data - for example if you have a required field added to an uploads node and did not provide a value to it every time you've added an upload node
BREAKING: this will now display errors if you're previously had invalid link or upload fields data - for example if you have a required field added to an uploads node and did not provide a value to it every time you've added an upload node
**BREAKING:**
- Drawer fields are no longer wrapped in a `fields` group. This might be breaking if you depend on them being in a field group in any way - potentially if you use custom link fields. This does not change how the data is saved
- If you pass in an array of custom fields to the link feature, those were previously added to the base fields. Now, they completely replace the base fields for consistency. If you want to ADD fields to the base fields now, you will have to pass in a function and spread `defaultFields` - similar to how adding your own features to lexical works
**Example Migration for ADDING fields to the link base fields:**
**Previous:**
```ts
LinkFeature({
fields: [
{
name: 'rel',
label: 'Rel Attribute',
type: 'select',
hasMany: true,
options: ['noopener', 'noreferrer', 'nofollow'],
admin: {
description:
'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
},
},
],
}),
```
**Now:**
```ts
LinkFeature({
fields: ({ defaultFields }) => [
...defaultFields,
{
name: 'rel',
label: 'Rel Attribute',
type: 'select',
hasMany: true,
options: ['noopener', 'noreferrer', 'nofollow'],
admin: {
description:
'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
},
},
],
}),
BREAKING:
- Lexical may introduce breaking changes in their updates. Please consult their changelog. One breaking change I noticed is that the SerializedParagraphNode now has a new, required textFormat property.
- Now that lexical supports ESM, all CJS-style imports have been changed to ESM-style imports. You may have to do the same in your codebase if you import from lexical core packages
BREAKING:
- Unpopulated lexical relationship, link and upload nodes now save the relationTo document ID under value instead of value.id. This matches the behavior of core relationship fields. This changes the shape of the saved JSON data
- Any custom features which add their own population promises need to be reworked. populationPromises no longer accepts the promises as a return value. Instead, it expects you to mutate the promises array which is passed through, which mimics the way it works in core
* fix: handles filter options in form state merge
* chore: fix and reintegrate fields-relationship e2e tests
* chore: update withMergedProps function for e2e tests
* chore: improve flakiness with access control test suite
* fix issue with redirecting from a drawer
* chore: watches for created id in drawers
---------
Co-authored-by: James <james@trbl.design>
* moved refresh permissions test suite to access control
* support for custom Save, SaveDraft and Publish buttons in admin config for collections and globals
* moved navigation content to client side so that permissions can be refreshed from active state
* test: passing point fields test suite
* chore: removes waits from point fields test suite
* chore: removes unnecessary waits in dates field test suite
* chore: removes waits entirely from dates tests
* chore: adds translates function for longitude/latitude
* chore: renames coordinate function and conditionally renders hypen in the function
* test: passing collapsible fields test suite
* chore: passes indexPath into ArrayRow & updates path in collapsible field
* fix: collapsible paths and indexPath prop types
* chore: improves path and schemaPath syntax
* leftover
* chore: updates selectors in collapsibles tests
* chore: updates selector in live-preview test suite
---------
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
* fix: only execute onChange if form modified
* fix: move document loading logic from RSC to DocumentInfoProvider
* fix: make it work for globals
* chore: remove unnecessary diffs
---------
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
BREAKING CHANGE: collection.admin.hooks.beforeDuplicate removed and instead should be handled using field beforeDuplicate hooks which take the full field hook arguments.
* feat: duplicate doc moved from frontend to backend concern
* feat: default beforeDuplicate hook functions on unique fields
* docs: beforeDuplicate field hook
* test: duplicate doc local api
* chore: fix build errors
* chore: add access.create call to duplicate operation
* chore: perfectionist reorder imports
* chore: attach mongoMemoryServer to db and destroy in tests
* bump mongodb-memory-server to 9.x
---------
Co-authored-by: Paul Popus <paul@nouance.io>
* fix: cannot get versions view for globals, return Unauthorized view if you are unauthorized instead of the Not Found view for document edit views. This makes it match the API
* chore: ensure there is always an error view to render if needed
* working playwright
* chore: use zipped, local build of playwright instead of patching it
* chore: remove bloat
* chore: get playwright and lexical to work by fixing imports from cjs modules
* chore: explores pattern for rscs in lexical
* WORKING!!!!!!
* fix(richtext-slate): field map path
* Working Link Drawer
* fix issues after merge
* AlignFeature
* Fix AlignFeature
---------
Co-authored-by: James <james@trbl.design>
* wip moves payload, user and data into partial req
* chore: adjust req type
* chore(next): installs sass and resolves type errors
* feat: working login route/view
* fix: me route
* chore(next): scaffolds access routes (#4562)
* chore(next): scaffolds admin layout and dashboard view (#4566)
* chore(next): builds initPage utility (#4589)
* feat(3.0): next route handlers (#4590)
* chore: removes old files
* chore(next): ssr list view (#4594)
* chore: removes old files
* chore: adjusts graphql file imports to align with new operation exports
* chore: allows for custom endpoints
* chore: cleanup
* chore(next): ssr edit view (#4614)
* chore(ui): ssr main nav (#4619)
* chore(next): ssr account view (#4620)
* chore(next): ssr auth views and document create (#4631)
* chore(next): ssr globals view (#4640)
* chore(next): scaffolds document layout (#4644)
* chore(next): ssr versions view (#4645)
* chore(next): ssr field conditions (#4675)
* chore(next): ssr field validations (#4700)
* chore(next): moves dashboard view into next dir
* chore(next): moves account view into next dir
* chore(next): moves global edit view into next dir
* chore(next): returns isolated configs and locale from initPage
* chore(next): ssr api view (#4721)
* feat: adds i18n functionality within Rest API, Local and Client contexts (#4749)
* chore: separate client translation groups with empty line
* chore: add missing translation used in db adapters
* chore: simplify next/routes export and import paths
* chore: renames PayloadT to Payload
* chore(next): custom views (#4748)
* chore: fix translation tsconfig
* chore: adjust other package ts-configs that rely on translations
* chore(next): installs @payloadcms/ui as direct dependency
* chore(next): progress to build
* chore(next): migrates types (#4792)
* fixes acccept-language detection
* chore(next): moves remaining components out from payload core (#4794)
* chore(deps): removes all unused dependencies from payload core (#4797)
* chore(next): achieves buildable state (#4803)
* adds Translation component and removes more react-i18next
* fixes up remaining translation strings
* fixes a few i18n TODO's
* chore: remaining translation strings without colons
* chore: adds missing ja translations
* chore(next): ssr group field (#4830)
* chore: removes placeholder t function
* chore: removes old file
* chore(bundler-webpack): removes webpack bundler
* chore(bundler-vite): removes vite bundler
* chore(next): ssr tabs field (#4863)
* chore(next): ssr row field
* chore(next): ssr textarea field
* chore(next): wires server action into document edit view (#4873)
* chore(next): conditional logic (#4880)
* chore(next): ssr radio, point, code, json, ui, and hidden fields (#4891)
* chore(next): ssr collapsible field (#4894)
* chore: remove findByID from req
* chore: adjusts file property on request type
* comment clarification
* chore: wires up busboy with Requst readstream
* chore: ports over express-fileupload into a NextJS compatible format
* chore: adjust upload file structure
* chore: adds try/catch around routes, corrects a few route responses
* chore: renames file/function
* chore: improve req type safety in local operations, misc req.files replacements
* chore: misc type and fn export changes
* chore: ensures root routes take pass unmodified request to root routes
* chore: improve types
* chore: consolidates locale api req initialization (#4922)
* chore(next): overhauls field rendering strategy (#4924)
* chore(next): ssr array field (#4937)
* chore(next): ssr blocks field (#4942)
* chore(next): ssr upload field and document drawer (#4957)
* chore(next): wires form submissions (#4982)
* chore: api handler adjustments
* feat: adds graphql playground handler
* adds credentials include setting to playground
* remove old playground init, stub graphql handler location
* fix: allow for null fallbackLocale
* fix: correctly prioritize locales passed as null
* chore: move all graphql code into next package
* graphql changes
* chore: semi working version of graphql http layer
* gql fix attempts
* rm console log
* chore: partial gql changes
* chore: adds gql and gql-http back into payload
* chore: removes collection from req
* chore: separates graphql package out for schema generation
* chore: dep cleanup
* chore: move graphql handlers
* chore: removes unused deps
* chore(next): ssr list view (#5032)
* chore: refactor response handler order for custom endpoints
* chore: add back in condition for collection GET path with 2 slugs
* chore: rm optional chain
* chore: import sort route file
* chore: allows custom endpoints to attempt before erroring
* feat: adds memoization to translation functions (#5036)
* chore: fix APIError import
* chore: return attemptCustomEndpointBeforeError responses
* chore(next): properly instantiates table columns
* fix(next): attaches params to req and properly assigns prefs key (#5042)
* chore: reorganize next route order
* chore(next): adds RouteError handler to next routes
* chore: builds payload successfully
* chore: misc file omissions
* fix(ui): maintains proper column order
* fix(ui): ensures first cell is a link
* fix(next): properly copies url object in createPayloadRequest (#5064)
* fix(ui): bumps react-toastify to v10.0.4 to fix hydration warnings
* feat: add route for static file GET requests (#5065)
* chore(next): allows resolved config promise to be thread through initPage (#5071)
* chore(ui): conditionally renders field label from props
* feat(next): next install script
* chore: pass config to route handlers
* feat: initial test suite framework (#4929)
* chore(next): renderable account, api, and create first user views (#5084)
* fix(next): properly parses search params in find, update, and delete handlers (#5088)
* chore(next): ssr versions view (#5085)
* chore: adds homepage for scss testing
* chore: moves dev folder to top, establishes new test pattern
* chore: working turbopack
* chore: sets up working dynamic payload-config imports
* remove unused code
* chore: rm console log
* misc
* feat: correctly subs out ability to boot REST API within same process
* chore: WIP dev suites
* chore: removes need for REST_API folder in test dir
* removes duplicate bootAdminPanel fn
* misc
* specify default export
* chore: sets up jest to work with next/jest
* chore: progress to mongodb and sharp builds
* chore: passing community tests
* chore: sorta workin
* chore: adjust payload-config import
* chore: adds rest client for Next handlers
* chore: removes test garb
* chore: restores payload-config tsconfig path temporarily
* chore: establishes pattern for memory db during tests
* chore: bumps mongoose to 7
* chore(next): 404s on nested create urls
* chore: functional _community e2e
* chore: increases e2e expect timeout
* fix(next): sanitizes locale toString from client config
* chore: type fixes
* chore: pulls mongodb from main
* chore: uses graphql to log user in
* feat: passing auth test suite
* chore(ui): threads params through context and conditionally renders document tabs (#5094)
* feat(ui): adds params context (#5095)
* chore: removes unecessary memory allocation for urlPropertiesObject object
* chore: passing graphql test suite
* chore: removes references to bson
* chore: re-enables mongodb memory server for auth test suite
* chore: replace bson with bson-objectid
* feat: passing collections-rest int suite
* chore: fixes bad imports
* chore: more passing int suites
* feat: passing globals int tests
* feat: passing hooks int test suite
* chore: remove last express file
* chore: start live-preview int test migration
* chore: passing localization int tests
* passing relationships int tests
* chore: partial passing upload int tests
* chore: fixes scss imports
* chore(ui): renders document info provider at root (#5106)
* chore: adds schema path to useFieldPath provider, more passing tests
* chore: begins work to optimize translation imports
* chore: add translations to ui ts-config references
* chore: add exports folder to package json exports
* chore: adds readme how-to-use instructions
* chore: attempts refactor of translation imports
* chore: adds authentication:account translation key to server keys
* chore: finishes translation optimization
* chore: ignores warnings from mongodb
* chore(ui): renders live document title (#5115)
* chore(ui): ssr document tabs (#5116)
* chore: handles redirecting from login
* chore: handle redirect with no searchParams
* chore: handle missing segments
* chore(next): migrates server action into standalone api endpoint (#5122)
* chore: adjust dashboard colection segments
* test: update e2e suites
* fix(ui): prevents unnecessary calls to form state
* chore: fix finding global config fields from schema path
* fix(next): executes root POST endpoints
* chore(ui): ignores values returned by form state polling
* chore: scaffolds ssr rte
* chore: renders client leaves
* chore: server-side rendered rich text elements
* chore: defines ClientFunction pattern
* chore(ui): migrates relationship field
* chore: adds translations, cleans up slate
* chore: functional slate link
* chore: slate upload ssr
* chore: relationship slate ssr
* chore: remaining slate ssr
* chore: fixes circular workspace dep
* chore: correct broken int test import paths
* chore: remove media files from root
* chore: server renders custom edit view
* fix(ui): resolves infinite loading in versions view
* fix(next): resolves global edit view lookup
* chore: payload builds
* chore: delete unused files
* chore: removes local property from payload
* chore: adds mongodb as dev dep in db-mongodb package
* chore: hide deprecation warnings for tempfile and jest-environment-jsdom
* chore: remove all translations from translations dist
* chore: clean ts-config files
* chore: simple type fixes
* chore(ui): server renders custom list view
* chore: fix next config payload-config alias
* chore: adds turbo alias paths
* chore: adjusts translation generation
* chore: improve auth function
* chore: eslint config for packages/ui
* chore(ui): exports FormState
* chore(next): migrates account view to latest patterns
* chore: disable barbie mode
* chore(ui): lints
* chore(next): lints
* chore: for alexical
* chore: custom handler type signature adjustment
* fix: non-boolean condition result causes infinite looping (#4579)
* chore(richtext-lexical): upgrade lexical from v0.12.5 to v0.12.6 (#4732)
* chore(richtext-lexical): upgrade all lexical packages from 0.12.5 to 0.12.6
* fix(richtext-lexical): fix TypeScript errors
* fix indenting
* feat(richtext-lexical): Blocks: generate type definitions for blocks fields (#4529)
* feat(richtext-lexical)!: Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground (#5066)
* feat(richtext-lexical): Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground
* chore: upgrade lexical version used in monorepo
* chore: remove the 3
* chore: upgrade nodemon versions (#5059)
* feat: add more options to addFieldStatePromise so that it can be used for field flattening (#4799)
* feat(plugin-seo)!: remove support for payload <2.7.0 (#4765)
* chore(plugin-seo): remove test script from package.json (#4762)
* chore: upgrade @types/nodemailer from v6.4.8 to v6.4.14 (#4733)
* chore: revert auth and initPage changes
* chore(next): moves edit and list views (#5170)
* fix: "The punycode module is deprecated" warning by updating nodemailer
* chore: adjust translations tsconfig paths in root
* chore: fix merge build
---------
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: Jarrod Flesch <30633324+JarrodMFlesch@users.noreply.github.com>
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
Co-authored-by: James <james@trbl.design>
Co-authored-by: Alessio Gravili <alessio@gravili.de>
Co-authored-by: Alessio Gravili <70709113+AlessioGr@users.noreply.github.com>
* feat(richtext-lexical): Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground
* chore: upgrade lexical version used in monorepo
This GitHub Action automatically comments on and/or labels Issues and PRs when a fix is released for them.
> [!IMPORTANT]
> 🔧 Heavily modified version of https://github.com/apexskier/github-release-commenter
## Fork Modifications
- Filters to closed PRs only
- Adds tag filter to support non-linear releases
- Better logging
- Moved to pnpm
- Uses @vercel/ncc for packaging
- Comments on locked issues by unlocking then re-locking
## How it works
Use this action in a workflow [triggered by a release](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#release). It will scan commits between that and the prior release, find associated Issues and PRs, and comment on them to let people know a release has been made. Associated Issues and PRs can be directly [linked](https://docs.github.com/en/free-pro-team@latest/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) to the commit or manually linked from a PR associated with the commit.
## Inputs
**GITHUB_TOKEN**
A GitHub personal access token with repo scope, such as [`secrets.GITHUB_TOKEN`](https://docs.github.com/en/free-pro-team@latest/actions/reference/authentication-in-a-workflow#about-the-github_token-secret).
**comment-template** (optional)
Override the comment posted on Issues and PRs. Set to the empty string to disable commenting. Several variables strings will be automatically replaced:
-`{release_link}` - a markdown link to the release
-`{release_name}` - the release's name
-`{release_tag}` - the release's tag
**label-template** (optional)
Add the given label. Multiple labels can be separated by commas. Several variable strings will be automatically replaced:
-`{release_name}` - the release's name
-`{release_tag}` - the release's tag
**skip-label** (optional)
Skip processing if any of the given labels are present. Same processing rules as **label-template**. Default is "dependencies".
## Example
```yml
on:
release:
types:[published]
jobs:
release:
steps:
- uses:apexskier/github-release-commenter@v1
with:
GITHUB_TOKEN:${{ secrets.GITHUB_TOKEN }}
comment-template:|
Release {release_link} addresses this.
```
## Known limitations
These are some known limitations of this action. I'd like to try to address them in the future.
- Non-linear releases aren't supported. For example, releasing a patch to a prior major release after a new major release has been bumped.
- Non-sequential releases aren't supported. For example, if you release multiple prereleases between two official releases, this will only create a comment for the first prerelease in which a fix is released, not the final release.
- The first release for a project will be ignored. This is intentional, as the use case is unlikely. Most projects will either have several alphas that don't need release comments, or won't use issues/PRs for the first commit.
- If a large number of things are commented on, you may see the error `Error: You have triggered an abuse detection mechanism. Please wait a few minutes before you try again.`. Consider using the `skip-label` input to reduce your load on the GitHub API.
## Versions
Workflows will automatically update the tags `v1` and `latest`, allowing you to reference one of those instead of locking to a specific release.
node-linker=isolated # due to a typescript bug, isolated mode requires @types/express-serve-static-core, terser and monaco-editor to be installed https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189 along with two other changes in the code which I've marked with (tsbugisolatedmode) in the code
node-linker=isolated
hoist-workspace-packages=false # the default in pnpm v9 is true, but that can break our runtime dependency checks
- **db-postgres:** configurable custom schema to use ([#5047](https://github.com/payloadcms/payload/issues/5047)) ([e8f2ca4](https://github.com/payloadcms/payload/commit/e8f2ca484ee56cd7767d5111e46ebd24752ff8de))
### Bug Fixes
- Add Context Provider in EditMany Component ([#5005](https://github.com/payloadcms/payload/issues/5005)) ([70e57fe](https://github.com/payloadcms/payload/commit/70e57fef184f7fcf56344ea755465f246f2253a5))
- **db-mongodb:** unique sparse for not required fields ([#5114](https://github.com/payloadcms/payload/issues/5114)) ([815bdfa](https://github.com/payloadcms/payload/commit/815bdfac0b0afbff2a20e54d5aee64b90f6b3a77))
- **db-postgres:** set \_parentID for array nested localized fields ([#5117](https://github.com/payloadcms/payload/issues/5117)) ([ceca5c4](https://github.com/payloadcms/payload/commit/ceca5c4e97f53f1346797a31b6abfc0375e98215))
- disabling API Key does not remove the key ([#5145](https://github.com/payloadcms/payload/issues/5145)) ([7a7f0ed](https://github.com/payloadcms/payload/commit/7a7f0ed7e8132253be607c111c160163b84bd770))
- handle thrown errors in config-level afterError hook ([#5147](https://github.com/payloadcms/payload/issues/5147)) ([32ed95e](https://github.com/payloadcms/payload/commit/32ed95e1ee87409db234f1b7bd6d2e462fd9ed5d))
- only replace the drawer content with full edit component if it exists ([#5144](https://github.com/payloadcms/payload/issues/5144)) ([0a07f60](https://github.com/payloadcms/payload/commit/0a07f607b9fb1217ad956cd05b2a84a4042a19ca))
- transaction error from access endpoint ([#5156](https://github.com/payloadcms/payload/issues/5156)) ([ad42d54](https://github.com/payloadcms/payload/commit/ad42d541b342ed56463b81cee6d6307df6f06d7f))
- **db-postgres:** adds idType to use uuid or serial id columns ([#3864](https://github.com/payloadcms/payload/issues/3864)) ([d6c2578](https://github.com/payloadcms/payload/commit/d6c25783cfa97983bf9db27ceb5ccd39a62c62f1))
- **db-postgres:** reconnect after disconnection from database ([#5086](https://github.com/payloadcms/payload/issues/5086)) ([bf942fd](https://github.com/payloadcms/payload/commit/bf942fdfa6ea9c26cf05295cc9db646bf31fa622))
- **plugin-search:** add req to beforeSync args for transactions ([#5068](https://github.com/payloadcms/payload/issues/5068)) ([98b87e2](https://github.com/payloadcms/payload/commit/98b87e22782c0a788f79326f22be05a6b176ad74))
- **richtext-lexical:** add justify aligment to AlignFeature ([#4035](https://github.com/payloadcms/payload/issues/4035)) ([#4868](https://github.com/payloadcms/payload/issues/4868)) ([6d6823c](https://github.com/payloadcms/payload/commit/6d6823c3e5609a58eeeeb8d043945a762f9463df))
- **richtext-lexical:** AddBlock handle for all nodes, even if they aren't empty paragraphs ([#5063](https://github.com/payloadcms/payload/issues/5063)) ([00fc034](https://github.com/payloadcms/payload/commit/00fc0343dabf184d5bab418d47c403b3ad11698f))
- **richtext-lexical:** Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground ([#5066](https://github.com/payloadcms/payload/issues/5066)) ([0d18822](https://github.com/payloadcms/payload/commit/0d18822062275c1826c8e2c3da2571a2b3483310))
- **db-postgres:** query using blockType ([#5044](https://github.com/payloadcms/payload/issues/5044)) ([35c2a08](https://github.com/payloadcms/payload/commit/35c2a085efa6d5ad59779960874bc9728a17e3a0))
- filterOptions errors cause transaction to abort ([#5079](https://github.com/payloadcms/payload/issues/5079)) ([5f3d016](https://github.com/payloadcms/payload/commit/5f3d0169bee21e1c0963dbd7ede9fe5f1c46a5a5))
- **plugin-form-builder:** hooks do not respect transactions ([#5069](https://github.com/payloadcms/payload/issues/5069)) ([82e9d31](https://github.com/payloadcms/payload/commit/82e9d31127c8df83c5bed92a5ffdab76d331900f))
- **richtext-lexical:** do not remove adjacent paragraph node when inserting certain nodes in empty editor ([#5061](https://github.com/payloadcms/payload/issues/5061)) ([6323965](https://github.com/payloadcms/payload/commit/6323965c652ea68dffeb716957b124d165b9ce96))
- **uploads:** account for serverURL when retrieving external file ([#5102](https://github.com/payloadcms/payload/issues/5102)) ([25cee8b](https://github.com/payloadcms/payload/commit/25cee8bb102bf80b3a4bfb4b4e46712722cc7f0d))
- **richtext-lexical:** Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground (#5066)
* You HAVE to make sure that any versions of the lexical packages (IF you have any installed) match the lexical version which richtext-lexical uses: v0.13.1. If you do not do this, you may be plagued by React useContext / "cannot find active editor state" errors
* Updates to lexical's API, e.g. the removal of INTERNAL_isPointSelection, could be breaking depending on your code. Please consult the [lexical changelog](https://github.com/facebook/lexical/blob/main/CHANGELOG.md).
- exposes collapsible provider with more functionality ([#5043](https://github.com/payloadcms/payload/issues/5043)) ([df39602](https://github.com/payloadcms/payload/commit/df39602758ae8dc3765bb48e51f7a657babfa559))
- **db-mongodb:** handle null values with exists ([#5037](https://github.com/payloadcms/payload/issues/5037)) ([cdc4cb9](https://github.com/payloadcms/payload/commit/cdc4cb971b9180ba2ed09741f5af1a3c18292828))
- **db-postgres:** handle nested docs with drafts ([#5012](https://github.com/payloadcms/payload/issues/5012)) ([da184d4](https://github.com/payloadcms/payload/commit/da184d40ece74bffb224002eb5df8f6987d65043))
- ensures docs with the same id are shown in relationship field select ([#4859](https://github.com/payloadcms/payload/issues/4859)) ([e1813fb](https://github.com/payloadcms/payload/commit/e1813fb884e0dc84203fcbab87527a99a4d3a5d7))
- query relationships by explicit id field ([#5022](https://github.com/payloadcms/payload/issues/5022)) ([a0a58e7](https://github.com/payloadcms/payload/commit/a0a58e7fd20dff54d210c968f4d5defd67441bdd))
- **richtext-lexical:** make editor reactive to initialValue changes ([#5010](https://github.com/payloadcms/payload/issues/5010)) ([2315781](https://github.com/payloadcms/payload/commit/2315781f1891ddde4b4c5f2f0cfa1c17af85b7a9))
- add more options to addFieldStatePromise so that it can be used for field flattening ([#4799](https://github.com/payloadcms/payload/issues/4799)) ([8725d41](https://github.com/payloadcms/payload/commit/8725d411645bb0270376e235669f46be2227ecc0))
- extend transactions to cover after and beforeOperation hooks ([#4960](https://github.com/payloadcms/payload/issues/4960)) ([1e8a6b7](https://github.com/payloadcms/payload/commit/1e8a6b7899f7b1e6451cc4d777602208478b483c))
- previousValue and previousSiblingDoc args added to beforeChange field hooks ([#4958](https://github.com/payloadcms/payload/issues/4958)) ([5d934ba](https://github.com/payloadcms/payload/commit/5d934ba02d07d98f781ce983228858ee5ce5c226))
- re-use existing logger instance passed to payload.init ([#3124](https://github.com/payloadcms/payload/issues/3124)) ([471d211](https://github.com/payloadcms/payload/commit/471d2113a790dc0d54b2f8ed84e6899310efd600))
- **richtext-lexical:** Blocks: generate type definitions for blocks fields ([#4529](https://github.com/payloadcms/payload/issues/4529)) ([90d7ee3](https://github.com/payloadcms/payload/commit/90d7ee3e6535d51290fc734b284ff3811dbda1f8))
- use deletion success message from server if provided ([#4966](https://github.com/payloadcms/payload/issues/4966)) ([e3c8105](https://github.com/payloadcms/payload/commit/e3c8105cc2ed6fdf8007d97cd7b5556fc71ed724))
### Bug Fixes
- **db-postgres:** filtering relationships with drafts enabled ([#4998](https://github.com/payloadcms/payload/issues/4998)) ([c3a3942](https://github.com/payloadcms/payload/commit/c3a39429697e9d335e9be199e7caafb82eb26219))
- **db-postgres:** handle schema changes with supabase ([#4968](https://github.com/payloadcms/payload/issues/4968)) ([5d3659d](https://github.com/payloadcms/payload/commit/5d3659d48ad8bbf5d96fbcd80434d2287cab97e0))
- **db-postgres:** indexes not created for non unique field names ([#4967](https://github.com/payloadcms/payload/issues/4967)) ([64f705c](https://github.com/payloadcms/payload/commit/64f705c3c94148972f67e8175e718015760d6430))
- **db-postgres:** indexes not creating for relationships, arrays, hasmany and blocks ([#4976](https://github.com/payloadcms/payload/issues/4976)) ([47106d5](https://github.com/payloadcms/payload/commit/47106d5a1af2ebd073fbbc6e474174c3d3835e5c))
- **db-postgres:** localized field sort count ([#4997](https://github.com/payloadcms/payload/issues/4997)) ([f3876c2](https://github.com/payloadcms/payload/commit/f3876c2a39efe19a1864213306725aadcc14f130))
- ensures docPermissions fallback to collection permissions on create ([#4969](https://github.com/payloadcms/payload/issues/4969)) ([afa2b94](https://github.com/payloadcms/payload/commit/afa2b942e0aad90c55744ae13e0ffe1cefa4585d))
- **migrations:** safely create migration file when no name passed ([#4995](https://github.com/payloadcms/payload/issues/4995)) ([0740d50](https://github.com/payloadcms/payload/commit/0740d5095ee1aef13e4e37f6b174d529f0f2d993))
- **plugin-seo:** tabbedUI with email field causes duplicate field ([#4944](https://github.com/payloadcms/payload/issues/4944)) ([db22cbd](https://github.com/payloadcms/payload/commit/db22cbdf21a39ed0604ab96c57ca4242eac82ce7))
- migrate down missing filter for latest batch ([#4860](https://github.com/payloadcms/payload/issues/4860)) ([b99d24f](https://github.com/payloadcms/payload/commit/b99d24fcfa698c493ea01c41621201abe18fabe3))
- **plugin-cloud-storage:** slow get file performance large collections ([#4927](https://github.com/payloadcms/payload/issues/4927)) ([f73d503](https://github.com/payloadcms/payload/commit/f73d503fecdfa5cefdc26ab9aad60b00563f881e))
- remove No Options dropdown from hasMany fields ([#4899](https://github.com/payloadcms/payload/issues/4899)) ([e5a7907](https://github.com/payloadcms/payload/commit/e5a7907a72c1371447ac2f71fce213ed22246092))
- upload input drawer does not show draft versions ([#4903](https://github.com/payloadcms/payload/issues/4903)) ([6930c4e](https://github.com/payloadcms/payload/commit/6930c4e9f2200853121391ad8f8df48ea66c40a4))
- **db-postgres:** Remove duplicate keys from response ([#4747](https://github.com/payloadcms/payload/issues/4747)) ([eb9e771](https://github.com/payloadcms/payload/commit/eb9e771a9ca03636486d36654f215b73435574cb))
- **db-postgres:** validateExistingBlockIsIdentical with arrays ([3b88adc](https://github.com/payloadcms/payload/commit/3b88adc7d0594af63ce190c40c9ee3905df67a31))
- **db-postgres:** validateExistingBlockIsIdentical with other tables ([0647c87](https://github.com/payloadcms/payload/commit/0647c870f15dc1b122734b678c2abeb6f56377d4))
- **plugin-seo:** fix missing spread operator in URL generator function ([#4723](https://github.com/payloadcms/payload/pull/4723))
- removes max-width from field-types class & correctly sets it on uploads ([#4829](https://github.com/payloadcms/payload/issues/4829)) ([ee5390a](https://github.com/payloadcms/payload/commit/ee5390aaca37a4154cde8392b60f091ec3e5175c))
- corrects config usage in build bin script ([#4796](https://github.com/payloadcms/payload/issues/4796)) ([775502b](https://github.com/payloadcms/payload/commit/775502b1616c1bd35a3044438e253a0e84219f99))
- allow custom config properties in blocks ([#4766](https://github.com/payloadcms/payload/issues/4766)) ([d92af29](https://github.com/payloadcms/payload/commit/d92af295ebe253160ac4c8fb788a1fb143ab85ae))
- **logger:** show local time ([#4663](https://github.com/payloadcms/payload/issues/4663)) ([493fde5](https://github.com/payloadcms/payload/commit/493fde5ccceb9a95d0b950a028a1d2f8888b4e64))
- **plugin-cloud:** use resend smtp instead of custom transport ([#4746](https://github.com/payloadcms/payload/issues/4746)) ([5cfde54](https://github.com/payloadcms/payload/commit/5cfde542b19988985746e220829d429a84ba3976))
- **plugin-seo:** remove support for payload <2.7.0 ([#4765](https://github.com/payloadcms/payload/issues/4765)) ([5e08368](https://github.com/payloadcms/payload/commit/5e083689d016fbff6c83419336e920f248932993))
### Bug Fixes
- allow a custom ID field to be nested inside unnamed tabs and rows ([#4701](https://github.com/payloadcms/payload/issues/4701)) ([6d5ac1d](https://github.com/payloadcms/payload/commit/6d5ac1de1ef55c4d51b253b4cf959bb703316c49))
- build payload without initializing ([#4028](https://github.com/payloadcms/payload/issues/4028)) ([1115387](https://github.com/payloadcms/payload/commit/11153877447af68389dde80fff2f9ee869468acb))
- passes `draft=true` in fetch for relationships ([#4784](https://github.com/payloadcms/payload/issues/4784)) ([0a259d2](https://github.com/payloadcms/payload/commit/0a259d27b5ef0d632ca54cd0a9ab99629f94c2a0))
- **plugin-form-builder:** replaces curly brackets with lexical editor ([#4753](https://github.com/payloadcms/payload/issues/4753)) ([8481846](https://github.com/payloadcms/payload/commit/84818469ea50d43276915d36bd92769422eadeb0))
- prioritizes `value` key when filtering / querying for relationships ([#4727](https://github.com/payloadcms/payload/issues/4727)) ([d0f7677](https://github.com/payloadcms/payload/commit/d0f7677d5ff2e0109fc348260d87e2606fdbd293))
- text hasMany validation ([#4789](https://github.com/payloadcms/payload/issues/4789)) ([e2e56a4](https://github.com/payloadcms/payload/commit/e2e56a4d58a9e1c31c05a0624f35642f58da162b))
### ⚠ BREAKING CHANGES
#### @payloadcms/plugin-seo
- remove support for payload <2.7.0 ([#4765](https://github.com/payloadcms/payload/pull/4765))
- **db-mongodb:** improve transaction support by passing req to migrations ([682eca2](https://github.com/payloadcms/payload/commit/682eca21860a4e2b2ab0bfd85613818790247224))
- **db-postgres:** improve transaction support by passing req to migrations ([555d027](https://github.com/payloadcms/payload/commit/555d02769a8731aeebbff9b67f9b0e1022904ade))
- hasMany property for text fields ([#4605](https://github.com/payloadcms/payload/issues/4605)) ([f43cf18](https://github.com/payloadcms/payload/commit/f43cf185d45b3c75fa0d78acd91e6cb60d87f166))
- improve transaction support by passing req to migrations ([1d14d9f](https://github.com/payloadcms/payload/commit/1d14d9f8b8ed077691175030182f094bb300ed17))
- provide document info to ActionsProvider ([#4696](https://github.com/payloadcms/payload/issues/4696)) ([6a8a6e4](https://github.com/payloadcms/payload/commit/6a8a6e4ef4913e0889e4d2eac82b28b9e4e8db22))
### Bug Fixes
- adds objectID validation to isValidID if of type `text` ([#4689](https://github.com/payloadcms/payload/issues/4689)) ([d419275](https://github.com/payloadcms/payload/commit/d419275fb50f0922307f2d3b4c0fcf80ac5ec98b))
- allow json field to be saved empty and reflect value changes ([#4687](https://github.com/payloadcms/payload/issues/4687)) ([0fb3a9c](https://github.com/payloadcms/payload/commit/0fb3a9ca89d1b63faea179bfa9b5b3d0a69c9398))
- custom ids in versions ([#4680](https://github.com/payloadcms/payload/issues/4680)) ([5d15955](https://github.com/payloadcms/payload/commit/5d15955f839d3f0cc557d8a8d7cc3a9e52e2f6b1))
- custom overrides of breadcrumb and parent fields ([7db58b4](https://github.com/payloadcms/payload/commit/7db58b482bba7e715c5be23cfe1a84295e95da29))
- **db-mongodb:** migration error calling beginTransaction with transactionOptions false ([21b9453](https://github.com/payloadcms/payload/commit/21b9453cf4e6eebf145d89a0190942015658413d))
- **db-mongodb:** querying plan for collections ignoring indexes ([#4655](https://github.com/payloadcms/payload/issues/4655)) ([63bc4ca](https://github.com/payloadcms/payload/commit/63bc4cabe1dea5f233aa1d9d4e64f3af93a8e081))
- **db-postgres:** incorrect results querying json field using exists operator ([9d9ac0e](https://github.com/payloadcms/payload/commit/9d9ac0ec28c97281bfdc7d6fb78c52baea492380))
- **db-postgres:** migrate down only runs latest batch size ([6acfae8](https://github.com/payloadcms/payload/commit/6acfae8ee7614746797e1fa91e1fd41c0240fdcd))
- **db-postgres:** query on json properties ([ec4d2f9](https://github.com/payloadcms/payload/commit/ec4d2f97cbf1c89d837372059bf3bb77f3ea6594))
- **db-postgres:** validation prevents group fields in blocks ([#4699](https://github.com/payloadcms/payload/issues/4699)) ([cab6bab](https://github.com/payloadcms/payload/commit/cab6babd608daeaabf9b63b1b446fded6804b60f))
- non-boolean condition result causes infinite looping ([#4579](https://github.com/payloadcms/payload/issues/4579)) ([a3e7816](https://github.com/payloadcms/payload/commit/a3e78161b551e8286063a173645a1d3dee162ad1))
- **plugin-form-builder:** slate serializer should replace curly braces in links ([#4703](https://github.com/payloadcms/payload/issues/4703)) ([28a3012](https://github.com/payloadcms/payload/commit/28a30120dd1aa3279fb2133aa0a0b1638d144be4))
- **plugin-nested-docs:** breadcrumbsFieldSlug used in resaveSelfAfterCreate hook ([a5a91c0](https://github.com/payloadcms/payload/commit/a5a91c08a9ade1482c512d3fa4c4f519ad85cf74))
- **plugin-nested-docs:** children wrongly publishing draft data ([#4692](https://github.com/payloadcms/payload/issues/4692)) ([5539942](https://github.com/payloadcms/payload/commit/55399424a13b1e0532d9eeefd09d442c107c3eda))
- **plugin-nested-docs:** custom parent field slug ([635e7c2](https://github.com/payloadcms/payload/commit/635e7c26e8b3b5138cf5a9bcb29e8ddd4b1e69b6))
- **plugin-nested-docs:** parent filterOptions errors when specifying breadcrumbsFieldSlug ([c4a4678](https://github.com/payloadcms/payload/commit/c4a4678afb097cf94c682595a78e416767a1fea8))
- relations with number based ids (postgres) show untitled ID: x ([1b91408](https://github.com/payloadcms/payload/commit/1b914083c8ee0c1b1d64fa7d4471ede0a24cfdb7))
- sidebar fields not disabled by access permissions ([#4682](https://github.com/payloadcms/payload/issues/4682)) ([85e38b7](https://github.com/payloadcms/payload/commit/85e38b7cfd5c0772344c4a8fb5100f7c48eb508f))
- unlock user condition always passes due to seconds conversion ([#4610](https://github.com/payloadcms/payload/issues/4610)) ([d543665](https://github.com/payloadcms/payload/commit/d543665995410256f77fe136173339aee6dcc7da))
- extend locales to have fallbackLocales ([9fac2ef](https://github.com/payloadcms/payload/commit/9fac2ef24e2ade4cf55b0d6a0e7f67e0edf57539))
### Bug Fixes
- "The punycode module is deprecated" warning by updating nodemailer ([00d8480](https://github.com/payloadcms/payload/commit/00d8480062d99dee56ef61a955f48a92efa6cbea))
- adjusts json field joi schema to allow editorOptions ([bff4cf5](https://github.com/payloadcms/payload/commit/bff4cf518f748efb9179f112c606d11d25db3d99))
- **db-postgres:** Wait for transaction to complete on commit ([#4582](https://github.com/payloadcms/payload/issues/4582)) ([a71d37b](https://github.com/payloadcms/payload/commit/a71d37b39806cd5956378a10246802d01d06c2dd))
- detect language from request headers accept-language ([#4656](https://github.com/payloadcms/payload/issues/4656)) ([69a9944](https://github.com/payloadcms/payload/commit/69a99445c9f1638a962a9c08ffe0bdc22e538bf6))
- navigation locks when modal is closed with esc ([#4664](https://github.com/payloadcms/payload/issues/4664)) ([be3beab](https://github.com/payloadcms/payload/commit/be3beabb9bafa137aa89e84cf47246017e969be8))
- req.locale and req.fallbackLocale get reassigned in local operations ([aa048d5](https://github.com/payloadcms/payload/commit/aa048d5409acd42b8f56367a16934085df9fbce2))
- resets actions array when navigating out of view with actions ([#4585](https://github.com/payloadcms/payload/issues/4585)) ([5c55231](https://github.com/payloadcms/payload/commit/5c5523195ccfa94a9bf42441e2a378f87836e64d))
- tab field error when using the same interface name ([#4657](https://github.com/payloadcms/payload/issues/4657)) ([f1fa374](https://github.com/payloadcms/payload/commit/f1fa374ed12b50fdf210f17ae1dda603f09a9726))
- add Chinese Traditional translation ([#4372](https://github.com/payloadcms/payload/issues/4372)) ([50253f6](https://github.com/payloadcms/payload/commit/50253f617c22d0d185bbac7f9d4304cddbc01f06))
- add context to auth and globals local API ([#4449](https://github.com/payloadcms/payload/issues/4449)) ([168d629](https://github.com/payloadcms/payload/commit/168d6296974042c3ff2a113f9f6c2bded7ba2b3e))
- adds new `actions` property to admin customization ([#4468](https://github.com/payloadcms/payload/issues/4468)) ([9e8f14a](https://github.com/payloadcms/payload/commit/9e8f14a897e77f6933eedb2410956a468f4187c3))
- async live preview urls ([#4339](https://github.com/payloadcms/payload/issues/4339)) ([5f17324](https://github.com/payloadcms/payload/commit/5f173241df6dc316d498767b1c81718e9c2b9a51))
- pass path to FieldDescription ([#4364](https://github.com/payloadcms/payload/issues/4364)) ([3b8a27d](https://github.com/payloadcms/payload/commit/3b8a27d199b3969cbca6ca750450798cb70f21e8))
- **plugin-form-builder:** Lexical support ([#4487](https://github.com/payloadcms/payload/issues/4487)) ([c6c5cab](https://github.com/payloadcms/payload/commit/c6c5cabfbb7eb954eea51170a6af7582b1f9b84b))
- prevent querying relationship when filterOptions returns false ([#4392](https://github.com/payloadcms/payload/issues/4392)) ([c1bd338](https://github.com/payloadcms/payload/commit/c1bd338d0d5e899f3892f1d18e355c00b265447a))
- **richtext-lexical:** improve floating select menu Dropdown classNames ([#4444](https://github.com/payloadcms/payload/issues/4444)) ([9331204](https://github.com/payloadcms/payload/commit/9331204295bfeaf7dd10bc075f42995b2cab2de4))
- **richtext-lexical:** improve link URL validation ([#4442](https://github.com/payloadcms/payload/issues/4442)) ([9babf68](https://github.com/payloadcms/payload/commit/9babf6804ce04d5828167eb8e7717727fe1cd472))
- **richtext-lexical:** lazy import React components to prevent client-only code from leaking into the server ([#4290](https://github.com/payloadcms/payload/issues/4290)) ([5de347f](https://github.com/payloadcms/payload/commit/5de347ffffca3bf38315d3d87d2ccc5c28cd2723))
- **richtext-lexical:** link node: change doc data format to be consistent with relationship field ([#4504](https://github.com/payloadcms/payload/issues/4504)) ([cc0ba89](https://github.com/payloadcms/payload/commit/cc0ba895188f40181c6ba3779f66d547d4ea66f9))
- **richtext-lexical:** rename TreeviewFeature into TreeViewFeature ([#4520](https://github.com/payloadcms/payload/issues/4520)) ([c49fd66](https://github.com/payloadcms/payload/commit/c49fd6692231b68ca61b079103a0fd7aa4673be1))
- **richtext-lexical:** Slate to Lexical converter: add blockquote conversion, convert custom link fields ([#4486](https://github.com/payloadcms/payload/issues/4486)) ([31f8f3c](https://github.com/payloadcms/payload/commit/31f8f3cac6bfd08f3adfa0a026a57c4b1b510045))
- **richtext-lexical:** Upload html serializer: Output picture element if the image has multiple sizes, improve absolute URL creation ([e558894](https://github.com/payloadcms/payload/commit/e55889480fceb8995646621923159d92de6e89c9))
### Bug Fixes
- adds bg color for year/month select options in datepicker ([#4508](https://github.com/payloadcms/payload/issues/4508)) ([07371b9](https://github.com/payloadcms/payload/commit/07371b9cad111999f2df4e1f709d6b95cd511c15))
- cursor jumping around inside json field ([#4453](https://github.com/payloadcms/payload/issues/4453)) ([6300037](https://github.com/payloadcms/payload/commit/63000373e66fb39443f882689e0ecf5c11ed8ad0))
- **db-postgres:** findOne correctly querying with where queries ([#4550](https://github.com/payloadcms/payload/issues/4550)) ([8bc31cd](https://github.com/payloadcms/payload/commit/8bc31cd5923517ab39ae1427aa0d0fb19d876dab))
- **db-postgres:** sorting on a not-configured field throws error ([#4382](https://github.com/payloadcms/payload/issues/4382)) ([dbaecda](https://github.com/payloadcms/payload/commit/dbaecda0e92fcb0fa67b4c5ac085e025f02de53a))
- defaultValues computed on new globals ([#4380](https://github.com/payloadcms/payload/issues/4380)) ([b6cffce](https://github.com/payloadcms/payload/commit/b6cffcea07b9fa21698b00b8bbed6f27197ded41))
- disallow duplicate fieldNames to be used on the same level in the config ([#4381](https://github.com/payloadcms/payload/issues/4381)) ([a1d66b8](https://github.com/payloadcms/payload/commit/a1d66b83e0dbea21e8da549b73cd25c537a57938))
- ensure ui fields do not make it into gql schemas ([#4457](https://github.com/payloadcms/payload/issues/4457)) ([3a20ddc](https://github.com/payloadcms/payload/commit/3a20ddc5f85162a316006f22ba66ee1c7aab99e3))
- format fields within tab for list controls ([#4516](https://github.com/payloadcms/payload/issues/4516)) ([2650c70](https://github.com/payloadcms/payload/commit/2650c70960a7374307a8862c3940c97d337d1d30))
- formats locales with multiple labels for versions locale selector ([#4495](https://github.com/payloadcms/payload/issues/4495)) ([8257661](https://github.com/payloadcms/payload/commit/8257661c47b5b968a57fb2228d7045d876a3f484))
- graphql schema generation for fields without queryable subfields ([#4463](https://github.com/payloadcms/payload/issues/4463)) ([13e3e06](https://github.com/payloadcms/payload/commit/13e3e0671353ca34e603fece57a12199f2082ca0))
- handles null upload field values ([#4397](https://github.com/payloadcms/payload/issues/4397)) ([cf9a370](https://github.com/payloadcms/payload/commit/cf9a3704df21ce8b32feb0680793cba804cd66f7))
- **live-preview:** populates rte uploads and relationships ([#4379](https://github.com/payloadcms/payload/issues/4379)) ([4090aeb](https://github.com/payloadcms/payload/commit/4090aebb0e94e776258f0c1c761044a4744a1857))
- **live-preview:** sends raw js objects through window.postMessage instead of json ([#4354](https://github.com/payloadcms/payload/issues/4354)) ([03a3872](https://github.com/payloadcms/payload/commit/03a387233d1b8876a2fcaa5f3b3fd5ed512c0bc4))
- make admin navigation transition smoother ([#4217](https://github.com/payloadcms/payload/issues/4217)) ([eb6572e](https://github.com/payloadcms/payload/commit/eb6572e9e56e680cad331c1bc5da47e91306deb9))
- omit field default value if read access returns false ([#4518](https://github.com/payloadcms/payload/issues/4518)) ([3e9ef84](https://github.com/payloadcms/payload/commit/3e9ef849cd8e69e1e8d7f2f653f0647e93c8ab39))
- pin ts-node versions which are causing swc errors ([#4447](https://github.com/payloadcms/payload/issues/4447)) ([b9c0248](https://github.com/payloadcms/payload/commit/b9c024882350d14edd57f0f662a2269ed37975e3))
- **plugin-form-builder:** removes use of slate in rich-text serializer ([#4451](https://github.com/payloadcms/payload/issues/4451)) ([3df52a8](https://github.com/payloadcms/payload/commit/3df52a88568622f8fafeabad47c7501229e4ea5f))
- **plugin-nested-docs:** properly exports field utilities ([#4462](https://github.com/payloadcms/payload/issues/4462)) ([1cc87bd](https://github.com/payloadcms/payload/commit/1cc87bd8ea575dfa2e1f5ce5b38414bbba95b2cb))
- **richtext-\*:** loosen RichTextAdapter types due to re-occuring ts strict mode errors ([#4416](https://github.com/payloadcms/payload/issues/4416)) ([48f1299](https://github.com/payloadcms/payload/commit/48f1299fcba3e3811c6a7f31499f238537f9a5e3))
- **richtext-lexical:** Blocks field: should not prompt for unsaved changes due to value comparison between null and non-existent props ([#4450](https://github.com/payloadcms/payload/issues/4450)) ([548e78c](https://github.com/payloadcms/payload/commit/548e78c598cb6d029e7cc40f80d9855754f043bc))
- **richtext-lexical:** do not add unnecessary paragraph before upload, relationship and blocks nodes ([#4441](https://github.com/payloadcms/payload/issues/4441)) ([5c2739e](https://github.com/payloadcms/payload/commit/5c2739ebd144620cfd4ff02531f5812dd62ac61d))
- **richtext-lexical:** lexicalHTML field not working when used inside of Blocks field ([128f9c4](https://github.com/payloadcms/payload/commit/128f9c4e7e6e20dd1ee221f49428a5bce5179c5f))
- **richtext-lexical:** lexicalHTML field now works when used inside of row fields ([#4440](https://github.com/payloadcms/payload/issues/4440)) ([0421173](https://github.com/payloadcms/payload/commit/0421173f9e2d6db1b6a94b25884ea807921f2d09))
- **richtext-lexical:** not all types of URLs are validated correctly ([ac7f980](https://github.com/payloadcms/payload/commit/ac7f9809bc2b9fb6a52b48c10f7d51414801e4de))
- searching by id sends undefined in where query param ([#4464](https://github.com/payloadcms/payload/issues/4464)) ([46e8c01](https://github.com/payloadcms/payload/commit/46e8c01fbed68856be68804f2bd9744c4c6f5a95))
- updates return value of empty arrays in getDataByPath ([#4553](https://github.com/payloadcms/payload/issues/4553)) ([f3748a1](https://github.com/payloadcms/payload/commit/f3748a1534a13e6d844aadd9f0e3e6acbe483d03))
- upload editing error with plugin-cloud ([#4170](https://github.com/payloadcms/payload/issues/4170)) ([fcbe574](https://github.com/payloadcms/payload/commit/fcbe5744d945dc43642cdaa2007ddc252ecafafa))
- upload related issues, cropping, fetching local file, external preview image ([#4461](https://github.com/payloadcms/payload/issues/4461)) ([45c472d](https://github.com/payloadcms/payload/commit/45c472d6b35c41e597038089ad1755cdb88193b6))
- uploads files after validation ([#4218](https://github.com/payloadcms/payload/issues/4218)) ([65adfd2](https://github.com/payloadcms/payload/commit/65adfd21ed538b79628dc4f8ce9e1a5a1bba6aed))
### ⚠ BREAKING CHANGES
#### @payloadcms/richtext-lexical
- **richtext-lexical:** rename TreeviewFeature into TreeViewFeature ([#4520](https://github.com/payloadcms/payload/issues/4520)) ([c49fd66](https://github.com/payloadcms/payload/commit/c49fd6692231b68ca61b079103a0fd7aa4673be1))
If you import TreeviewFeature, you have to rename the import to use TreeViewFeature (capitalized "V")
- **richtext-lexical:** link node: change doc data format to be consistent with relationship field ([#4504](https://github.com/payloadcms/payload/issues/4504)) ([cc0ba89](https://github.com/payloadcms/payload/commit/cc0ba895188f40181c6ba3779f66d547d4ea66f9))
An unpopulated, internal link node no longer saves the doc id under fields.doc.value.id. Now, it saves it under fields.doc.value.
Migration inside of payload is automatic. If you are reading from the link node inside of your frontend though, you will have to adjust it.
- **richtext-lexical:** improve floating select menu Dropdown classNames ([#4444](https://github.com/payloadcms/payload/issues/4444)) ([9331204](https://github.com/payloadcms/payload/commit/9331204295bfeaf7dd10bc075f42995b2cab2de4))
Dropdown component has a new mandatory sectionKey prop
- **richtext-lexical:** lazy import React components to prevent client-only code from leaking into the server ([#4290](https://github.com/payloadcms/payload/issues/4290)) ([5de347f](https://github.com/payloadcms/payload/commit/5de347ffffca3bf38315d3d87d2ccc5c28cd2723))
1. Most important: If you are updating `@payloadcms/richtext-lexical` to v0.4.0 or higher, you will HAVE to update payload to the latest version as well. If you don't update it, payload likely won't start up due to validation errors. It's generally good practice to upgrade packages prefixed with @payloadcms/ together with payload and keep the versions in sync.
2.`@payloadcms/richtext-slate` is not affected by this.
3. Every single property in the `Feature` interface which accepts a React component now no longer accepts a React component, but a function which imports a React component instead. This is done to ensure no unnecessary client-only code is leaked to the server when importing Features on a server.
Or alternatively, if you're using default exports instead of named exports:
```ts
// import BlockIcon from '../../lexical/ui/icons/Block' // <= Remove this import
...
Icon:()=>
// @ts-expect-error
import('../../lexical/ui/icons/Block'),
```
4. The types for `SanitizedEditorConfig` and `EditorConfig` have changed. Their respective `lexical` property no longer expects the `LexicalEditorConfig`. It now expects a function returning the `LexicalEditorConfig`. You will have to adjust this if you adjusted that property anywhere, e.g. when initializing the lexical field editor property, or when initializing a new headless editor.
5. The following exports are now exported from the `@payloadcms/richtext-lexical/components` subpath exports instead of `@payloadcms/richtext-lexical`:
-`ToolbarButton`
-`ToolbarDropdown`
-`RichTextCell`
-`RichTextField`
-`defaultEditorLexicalConfig`
You will have to adjust your imports, only if you import any of those properties in your project.
* **richtext-*:** loosen RichTextAdapter types due to re-occuring ts strict mode errors ([#4416](https://github.com/payloadcms/payload/issues/4416)) ([48f1299](https://github.com/payloadcms/payload/commit/48f1299fcba3e3811c6a7f31499f238537f9a5e3))
* **richtext-lexical:** lexicalHTML field not working when used inside of Blocks field ([128f9c4](https://github.com/payloadcms/payload/commit/128f9c4e7e6e20dd1ee221f49428a5bce5179c5f))
- **richtext-\*:** loosen RichTextAdapter types due to re-occuring ts strict mode errors ([#4416](https://github.com/payloadcms/payload/issues/4416)) ([48f1299](https://github.com/payloadcms/payload/commit/48f1299fcba3e3811c6a7f31499f238537f9a5e3))
- **richtext-lexical:** lexicalHTML field not working when used inside of Blocks field ([128f9c4](https://github.com/payloadcms/payload/commit/128f9c4e7e6e20dd1ee221f49428a5bce5179c5f))
* add Chinese Traditional translation ([#4372](https://github.com/payloadcms/payload/issues/4372)) ([50253f6](https://github.com/payloadcms/payload/commit/50253f617c22d0d185bbac7f9d4304cddbc01f06))
* async live preview urls ([#4339](https://github.com/payloadcms/payload/issues/4339)) ([5f17324](https://github.com/payloadcms/payload/commit/5f173241df6dc316d498767b1c81718e9c2b9a51))
* pass path to FieldDescription ([#4364](https://github.com/payloadcms/payload/issues/4364)) ([3b8a27d](https://github.com/payloadcms/payload/commit/3b8a27d199b3969cbca6ca750450798cb70f21e8))
* **richtext-lexical:** lazy import React components to prevent client-only code from leaking into the server ([#4290](https://github.com/payloadcms/payload/issues/4290)) ([5de347f](https://github.com/payloadcms/payload/commit/5de347ffffca3bf38315d3d87d2ccc5c28cd2723))
- add Chinese Traditional translation ([#4372](https://github.com/payloadcms/payload/issues/4372)) ([50253f6](https://github.com/payloadcms/payload/commit/50253f617c22d0d185bbac7f9d4304cddbc01f06))
- async live preview urls ([#4339](https://github.com/payloadcms/payload/issues/4339)) ([5f17324](https://github.com/payloadcms/payload/commit/5f173241df6dc316d498767b1c81718e9c2b9a51))
- pass path to FieldDescription ([#4364](https://github.com/payloadcms/payload/issues/4364)) ([3b8a27d](https://github.com/payloadcms/payload/commit/3b8a27d199b3969cbca6ca750450798cb70f21e8))
- **richtext-lexical:** lazy import React components to prevent client-only code from leaking into the server ([#4290](https://github.com/payloadcms/payload/issues/4290)) ([5de347f](https://github.com/payloadcms/payload/commit/5de347ffffca3bf38315d3d87d2ccc5c28cd2723))
* **db-postgres:** sorting on a not-configured field throws error ([#4382](https://github.com/payloadcms/payload/issues/4382)) ([dbaecda](https://github.com/payloadcms/payload/commit/dbaecda0e92fcb0fa67b4c5ac085e025f02de53a))
* defaultValues computed on new globals ([#4380](https://github.com/payloadcms/payload/issues/4380)) ([b6cffce](https://github.com/payloadcms/payload/commit/b6cffcea07b9fa21698b00b8bbed6f27197ded41))
* handles null upload field values ([#4397](https://github.com/payloadcms/payload/issues/4397)) ([cf9a370](https://github.com/payloadcms/payload/commit/cf9a3704df21ce8b32feb0680793cba804cd66f7))
* **live-preview:** populates rte uploads and relationships ([#4379](https://github.com/payloadcms/payload/issues/4379)) ([4090aeb](https://github.com/payloadcms/payload/commit/4090aebb0e94e776258f0c1c761044a4744a1857))
* **live-preview:** sends raw js objects through window.postMessage instead of json ([#4354](https://github.com/payloadcms/payload/issues/4354)) ([03a3872](https://github.com/payloadcms/payload/commit/03a387233d1b8876a2fcaa5f3b3fd5ed512c0bc4))
* upload editing error with plugin-cloud ([#4170](https://github.com/payloadcms/payload/issues/4170)) ([fcbe574](https://github.com/payloadcms/payload/commit/fcbe5744d945dc43642cdaa2007ddc252ecafafa))
* uploads files after validation ([#4218](https://github.com/payloadcms/payload/issues/4218)) ([65adfd2](https://github.com/payloadcms/payload/commit/65adfd21ed538b79628dc4f8ce9e1a5a1bba6aed))
- **db-postgres:** sorting on a not-configured field throws error ([#4382](https://github.com/payloadcms/payload/issues/4382)) ([dbaecda](https://github.com/payloadcms/payload/commit/dbaecda0e92fcb0fa67b4c5ac085e025f02de53a))
- defaultValues computed on new globals ([#4380](https://github.com/payloadcms/payload/issues/4380)) ([b6cffce](https://github.com/payloadcms/payload/commit/b6cffcea07b9fa21698b00b8bbed6f27197ded41))
- handles null upload field values ([#4397](https://github.com/payloadcms/payload/issues/4397)) ([cf9a370](https://github.com/payloadcms/payload/commit/cf9a3704df21ce8b32feb0680793cba804cd66f7))
- **live-preview:** populates rte uploads and relationships ([#4379](https://github.com/payloadcms/payload/issues/4379)) ([4090aeb](https://github.com/payloadcms/payload/commit/4090aebb0e94e776258f0c1c761044a4744a1857))
- **live-preview:** sends raw js objects through window.postMessage instead of json ([#4354](https://github.com/payloadcms/payload/issues/4354)) ([03a3872](https://github.com/payloadcms/payload/commit/03a387233d1b8876a2fcaa5f3b3fd5ed512c0bc4))
* ensure doc controls are not hidden behind lexical field ([#4345](https://github.com/payloadcms/payload/issues/4345)) ([bea79fe](https://github.com/payloadcms/payload/commit/bea79feaeaee18bf94dd04262f134483f1468494))
* query validation on relationship fields ([#4353](https://github.com/payloadcms/payload/issues/4353)) ([fe888b5](https://github.com/payloadcms/payload/commit/fe888b5f6ceaa3969eac759cbdfb109b106dae05))
* **richtext-lexical:** blocks content may be hidden behind components outside of the editor ([#4325](https://github.com/payloadcms/payload/issues/4325)) ([3e745e9](https://github.com/payloadcms/payload/commit/3e745e91da620a00e3f0f91892ee3ec66ba72bc0))
* **richtext-lexical:** Blocks node: incorrect conversion from v1 node to v2 node ([ef84a2c](https://github.com/payloadcms/payload/commit/ef84a2cfffbb1be52dd948e59eeec0ce324e9046))
- ensure doc controls are not hidden behind lexical field ([#4345](https://github.com/payloadcms/payload/issues/4345)) ([bea79fe](https://github.com/payloadcms/payload/commit/bea79feaeaee18bf94dd04262f134483f1468494))
- query validation on relationship fields ([#4353](https://github.com/payloadcms/payload/issues/4353)) ([fe888b5](https://github.com/payloadcms/payload/commit/fe888b5f6ceaa3969eac759cbdfb109b106dae05))
- **richtext-lexical:** blocks content may be hidden behind components outside of the editor ([#4325](https://github.com/payloadcms/payload/issues/4325)) ([3e745e9](https://github.com/payloadcms/payload/commit/3e745e91da620a00e3f0f91892ee3ec66ba72bc0))
- **richtext-lexical:** Blocks node: incorrect conversion from v1 node to v2 node ([ef84a2c](https://github.com/payloadcms/payload/commit/ef84a2cfffbb1be52dd948e59eeec0ce324e9046))
* add serbian (latin and cyrillic) translations ([#4268](https://github.com/payloadcms/payload/issues/4268)) ([40c8909](https://github.com/payloadcms/payload/commit/40c8909ee0003d45a1afa4524ade557268d01067))
* **db-mongodb:** search for migrations dir intelligently ([530c825](https://github.com/payloadcms/payload/commit/530c825f806708df8672e66c8e9c559c5e625e5e))
* **db-postgres:** search for migrations dir intelligently ([308979f](https://github.com/payloadcms/payload/commit/308979f31d27979955a52f32be4ea33849b48f30))
* **live-preview:** batches api requests ([#4315](https://github.com/payloadcms/payload/issues/4315)) ([d49bb43](https://github.com/payloadcms/payload/commit/d49bb4351f22f17f68477c3145594abbb60fab05))
* support migrations with js files ([2122242](https://github.com/payloadcms/payload/commit/21222421929ae19728c31bdccc84995ce3951224))
* support OAuth 2.0 format Authorization: Bearer tokens in headers ([c1eb9d1](https://github.com/payloadcms/payload/commit/c1eb9d1727daf96375e73943882621127b78e593))
- add serbian (latin and cyrillic) translations ([#4268](https://github.com/payloadcms/payload/issues/4268)) ([40c8909](https://github.com/payloadcms/payload/commit/40c8909ee0003d45a1afa4524ade557268d01067))
- **db-mongodb:** search for migrations dir intelligently ([530c825](https://github.com/payloadcms/payload/commit/530c825f806708df8672e66c8e9c559c5e625e5e))
- **db-postgres:** search for migrations dir intelligently ([308979f](https://github.com/payloadcms/payload/commit/308979f31d27979955a52f32be4ea33849b48f30))
- **live-preview:** batches api requests ([#4315](https://github.com/payloadcms/payload/issues/4315)) ([d49bb43](https://github.com/payloadcms/payload/commit/d49bb4351f22f17f68477c3145594abbb60fab05))
- support migrations with js files ([2122242](https://github.com/payloadcms/payload/commit/21222421929ae19728c31bdccc84995ce3951224))
- support OAuth 2.0 format Authorization: Bearer tokens in headers ([c1eb9d1](https://github.com/payloadcms/payload/commit/c1eb9d1727daf96375e73943882621127b78e593))
* **db-postgres:** allow for nested block fields to be queried ([#4237](https://github.com/payloadcms/payload/issues/4237)) ([cd07873](https://github.com/payloadcms/payload/commit/cd07873fc544766b4aeeff873dfb8d6e3e97e9dc))
* **db-postgres:** error saving nested arrays with versions ([#4302](https://github.com/payloadcms/payload/issues/4302)) ([3514bfb](https://github.com/payloadcms/payload/commit/3514bfbdaee99341ae739d03591cb63bd9415fe3))
* duplicate documents with required localized fields ([#4236](https://github.com/payloadcms/payload/issues/4236)) ([9da9b1f](https://github.com/payloadcms/payload/commit/9da9b1fc5050d4f29bcf6dce2f22027834aaf698))
* incorrect key property in Tabs field component ([#4311](https://github.com/payloadcms/payload/issues/4311)) ([3502ce7](https://github.com/payloadcms/payload/commit/3502ce720b3020eed5fc733884b525303faa4c15)), closes [#4282](https://github.com/payloadcms/payload/issues/4282)
* **live-preview-react:** removes payload from peer deps ([7e1052f](https://github.com/payloadcms/payload/commit/7e1052fd98c88a4d68af08f98ccc8936edb8ebf6))
* properly sets tabs key in fieldSchemaToJSON ([#4317](https://github.com/payloadcms/payload/issues/4317)) ([9cc88bb](https://github.com/payloadcms/payload/commit/9cc88bb47443ecdf525f4c99d9f13d81c141c471))
* **richtext-lexical:** HTML Converter field not working inside of tabs ([#4293](https://github.com/payloadcms/payload/issues/4293)) ([5bf6493](https://github.com/payloadcms/payload/commit/5bf64933b4b99a0ac8ef7d1d91d0165a16636a9f))
* **richtext-lexical:** re-use payload population logic to fix population-related issues ([#4291](https://github.com/payloadcms/payload/issues/4291)) ([094d02c](https://github.com/payloadcms/payload/commit/094d02ce1d85106470a1a8c6ffe9050873f2e57a))
* **richtext-slate:** add use client to top of tsx files importing from payload core ([#4312](https://github.com/payloadcms/payload/issues/4312)) ([d4f28b1](https://github.com/payloadcms/payload/commit/d4f28b16b4d42f224e9c5e4254f9ec55107a2f97))
- **db-postgres:** allow for nested block fields to be queried ([#4237](https://github.com/payloadcms/payload/issues/4237)) ([cd07873](https://github.com/payloadcms/payload/commit/cd07873fc544766b4aeeff873dfb8d6e3e97e9dc))
- **db-postgres:** error saving nested arrays with versions ([#4302](https://github.com/payloadcms/payload/issues/4302)) ([3514bfb](https://github.com/payloadcms/payload/commit/3514bfbdaee99341ae739d03591cb63bd9415fe3))
- duplicate documents with required localized fields ([#4236](https://github.com/payloadcms/payload/issues/4236)) ([9da9b1f](https://github.com/payloadcms/payload/commit/9da9b1fc5050d4f29bcf6dce2f22027834aaf698))
- incorrect key property in Tabs field component ([#4311](https://github.com/payloadcms/payload/issues/4311)) ([3502ce7](https://github.com/payloadcms/payload/commit/3502ce720b3020eed5fc733884b525303faa4c15)), closes [#4282](https://github.com/payloadcms/payload/issues/4282)
- **live-preview-react:** removes payload from peer deps ([7e1052f](https://github.com/payloadcms/payload/commit/7e1052fd98c88a4d68af08f98ccc8936edb8ebf6))
- properly sets tabs key in fieldSchemaToJSON ([#4317](https://github.com/payloadcms/payload/issues/4317)) ([9cc88bb](https://github.com/payloadcms/payload/commit/9cc88bb47443ecdf525f4c99d9f13d81c141c471))
- **richtext-lexical:** HTML Converter field not working inside of tabs ([#4293](https://github.com/payloadcms/payload/issues/4293)) ([5bf6493](https://github.com/payloadcms/payload/commit/5bf64933b4b99a0ac8ef7d1d91d0165a16636a9f))
- **richtext-lexical:** re-use payload population logic to fix population-related issues ([#4291](https://github.com/payloadcms/payload/issues/4291)) ([094d02c](https://github.com/payloadcms/payload/commit/094d02ce1d85106470a1a8c6ffe9050873f2e57a))
- **richtext-slate:** add use client to top of tsx files importing from payload core ([#4312](https://github.com/payloadcms/payload/issues/4312)) ([d4f28b1](https://github.com/payloadcms/payload/commit/d4f28b16b4d42f224e9c5e4254f9ec55107a2f97))
### BREAKING CHANGES
@@ -139,266 +464,251 @@ Here are the breaking changes and how to migrate:
2. The `title` property of `SlashMenuGroup` has been replaced by a new, mandatory `key` and an optional `displayName` property. To migrate, you will have to remove the `title` property and add a `key` property instead - make sure you do not use spaces or special characters in the `key`.
3. Additionally, if you have custom styles targeting elements inside of slash or floating-select-toolbar menus, you will have to adjust those too, as the CSS classes changed
[This is an example of performing these updates](https://github.com/payloadcms/payload/pull/4257/files#diff-dc2e7f503dd7076dff1d810da7ec77b8fc6a9e41127df4a417dece1b6e1587a0L61)
* **i18n:** adds Korean translation ([#4258](https://github.com/payloadcms/payload/issues/4258)) ([1401718](https://github.com/payloadcms/payload/commit/1401718b3b549ce1454389a982474dbe159eb61f))
- **i18n:** adds Korean translation ([#4258](https://github.com/payloadcms/payload/issues/4258)) ([1401718](https://github.com/payloadcms/payload/commit/1401718b3b549ce1454389a982474dbe159eb61f))
### Bug Fixes
* number field validation ([#4233](https://github.com/payloadcms/payload/issues/4233)) ([19fcfc2](https://github.com/payloadcms/payload/commit/19fcfc27af2ecb68ff989dcaed19b7b7d041a322))
* passes date options to the react-datepicker in filter UI, removes duplicate options from operators select ([#4225](https://github.com/payloadcms/payload/issues/4225)) ([3d2b62b](https://github.com/payloadcms/payload/commit/3d2b62b2100e36a54adc6a675257a4d671fdd469))
* prevent json data getting reset when switching tabs ([#4123](https://github.com/payloadcms/payload/issues/4123)) ([1dcd3a2](https://github.com/payloadcms/payload/commit/1dcd3a27825ed9d276b997a66f84bb2c05e87955))
* transactions broken within doc access ([443847e](https://github.com/payloadcms/payload/commit/443847ec716a3b87032d9d1904b6c90aadd47197))
* typo in polish translations ([#4234](https://github.com/payloadcms/payload/issues/4234)) ([56a4692](https://github.com/payloadcms/payload/commit/56a469266207ef83053b0c9176d1be4fc26087e6))
- number field validation ([#4233](https://github.com/payloadcms/payload/issues/4233)) ([19fcfc2](https://github.com/payloadcms/payload/commit/19fcfc27af2ecb68ff989dcaed19b7b7d041a322))
- passes date options to the react-datepicker in filter UI, removes duplicate options from operators select ([#4225](https://github.com/payloadcms/payload/issues/4225)) ([3d2b62b](https://github.com/payloadcms/payload/commit/3d2b62b2100e36a54adc6a675257a4d671fdd469))
- prevent json data getting reset when switching tabs ([#4123](https://github.com/payloadcms/payload/issues/4123)) ([1dcd3a2](https://github.com/payloadcms/payload/commit/1dcd3a27825ed9d276b997a66f84bb2c05e87955))
- transactions broken within doc access ([443847e](https://github.com/payloadcms/payload/commit/443847ec716a3b87032d9d1904b6c90aadd47197))
- typo in polish translations ([#4234](https://github.com/payloadcms/payload/issues/4234)) ([56a4692](https://github.com/payloadcms/payload/commit/56a469266207ef83053b0c9176d1be4fc26087e6))
* make outputSchema optional on richtext config ([#4230](https://github.com/payloadcms/payload/issues/4230)) ([3a784a0](https://github.com/payloadcms/payload/commit/3a784a06cc6c42c96b8d6cf023d942e6661be7b5))
- make outputSchema optional on richtext config ([#4230](https://github.com/payloadcms/payload/issues/4230)) ([3a784a0](https://github.com/payloadcms/payload/commit/3a784a06cc6c42c96b8d6cf023d942e6661be7b5))
* allow richtext adapters to control type generation, improve generated lexical types ([#4036](https://github.com/payloadcms/payload/issues/4036)) ([989c10e](https://github.com/payloadcms/payload/commit/989c10e0e0b36a8c34822263b19f5cb4b9ed6e72))
* hide publish button based on permissions ([#4203](https://github.com/payloadcms/payload/issues/4203)) ([de02490](https://github.com/payloadcms/payload/commit/de02490231fbc8936973c1b81ac87add39878d8b))
* **richtext-lexical:** Add new position: 'top' property for plugins ([eed4f43](https://github.com/payloadcms/payload/commit/eed4f4361cd012adf4e777820adbe7ad330ffef6))
- allow richtext adapters to control type generation, improve generated lexical types ([#4036](https://github.com/payloadcms/payload/issues/4036)) ([989c10e](https://github.com/payloadcms/payload/commit/989c10e0e0b36a8c34822263b19f5cb4b9ed6e72))
- hide publish button based on permissions ([#4203](https://github.com/payloadcms/payload/issues/4203)) ([de02490](https://github.com/payloadcms/payload/commit/de02490231fbc8936973c1b81ac87add39878d8b))
- **richtext-lexical:** Add new position: 'top' property for plugins ([eed4f43](https://github.com/payloadcms/payload/commit/eed4f4361cd012adf4e777820adbe7ad330ffef6))
### Bug Fixes
* fully define the define property for esbuild string replacement ([#4099](https://github.com/payloadcms/payload/issues/4099)) ([e22b95b](https://github.com/payloadcms/payload/commit/e22b95bdf3b2911ae67a07a76ec109c76416ea56))
* improves live preview breakpoints and zoom options in dark mode ([#4090](https://github.com/payloadcms/payload/issues/4090)) ([b91711a](https://github.com/payloadcms/payload/commit/b91711a74ad9379ed820b6675060209626b1c2d0))
* **plugin-nested-docs:** await populate breadcrumbs on resaveChildren ([#4226](https://github.com/payloadcms/payload/issues/4226)) ([4e41dd1](https://github.com/payloadcms/payload/commit/4e41dd1bf2706001fa03130adb1c69403795ac96))
* **richtext-lexical:** Blocks: Array row data is not removed ([#4209](https://github.com/payloadcms/payload/issues/4209)) ([0af9c4d](https://github.com/payloadcms/payload/commit/0af9c4d3985a6c46a071ef5ac28c8359cb320571))
* **richtext-lexical:** Blocks: fields without fulfilled condition are now skipped for validation ([50fab90](https://github.com/payloadcms/payload/commit/50fab902bd7baa1702ae0d995b4f58c1f5fca374))
* **richtext-lexical:** Blocks: make sure fields are wrapped in a uniquely-named group, change block node data format, fix react key error ([#3995](https://github.com/payloadcms/payload/issues/3995)) ([c068a87](https://github.com/payloadcms/payload/commit/c068a8784ec5780dbdca5416b25ba654afd05458))
* **richtext-lexical:** Blocks: z-index issue, e.g. select field dropdown in blocks hidden behind blocks below, or slash menu inside nested editor hidden behind blocks below ([09f17f4](https://github.com/payloadcms/payload/commit/09f17f44508539cfcb8722f7f462ef40d9ed54fd))
* **richtext-lexical:** Floating Select Toolbar: Buttons and Dropdown Buttons not clickable in nested editors ([615702b](https://github.com/payloadcms/payload/commit/615702b858e76994a174159cb69f034ef811e016)), closes [#4025](https://github.com/payloadcms/payload/issues/4025)
* **richtext-lexical:** incorrect caret positioning when selecting second line of multi-line paragraph ([#4165](https://github.com/payloadcms/payload/issues/4165)) ([b210af4](https://github.com/payloadcms/payload/commit/b210af46968b77d96ffd6ef60adc3b8d8bdc9376))
* **richtext-lexical:** make lexicalHTML() function work for globals ([dbfc835](https://github.com/payloadcms/payload/commit/dbfc83520ca8b5e55198a3c4b517ae3a80f9cac6))
* **richtext-lexical:** nested editor may lose focus when writing ([#4139](https://github.com/payloadcms/payload/issues/4139)) ([859c2f4](https://github.com/payloadcms/payload/commit/859c2f4a6d299a42e572133502b3841a74a11002))
* **richtext-lexical:** remove optional chaining after `this` as transpilers are not handling it well ([#4145](https://github.com/payloadcms/payload/issues/4145)) ([2c8d34d](https://github.com/payloadcms/payload/commit/2c8d34d2aadf2fcaf0655c0abef233f341d9945f))
* **richtext-lexical:** visual bug after rearranging blocks ([a6b4860](https://github.com/payloadcms/payload/commit/a6b486007dc26195adc5d576d937e35471c2868f))
* simplifies block/array/hasMany-number field validations ([#4052](https://github.com/payloadcms/payload/issues/4052)) ([803a37e](https://github.com/payloadcms/payload/commit/803a37eaa947397fa0a93b9f4f7d702c6b94ceaa))
* thread locale through to access routes from admin panel ([#4183](https://github.com/payloadcms/payload/issues/4183)) ([05f3169](https://github.com/payloadcms/payload/commit/05f3169a75b3b62962e7fe7842fbb6df6699433d))
* transactionID isolation for GraphQL ([#4095](https://github.com/payloadcms/payload/issues/4095)) ([195a952](https://github.com/payloadcms/payload/commit/195a952c4314e0d53fd579517035373b49d6ccae))
* upload fit not accounted for when editing focal point or crop ([#4142](https://github.com/payloadcms/payload/issues/4142)) ([45e9a55](https://github.com/payloadcms/payload/commit/45e9a559bbb16b2171465c8a439044011cebf102))
- fully define the define property for esbuild string replacement ([#4099](https://github.com/payloadcms/payload/issues/4099)) ([e22b95b](https://github.com/payloadcms/payload/commit/e22b95bdf3b2911ae67a07a76ec109c76416ea56))
- improves live preview breakpoints and zoom options in dark mode ([#4090](https://github.com/payloadcms/payload/issues/4090)) ([b91711a](https://github.com/payloadcms/payload/commit/b91711a74ad9379ed820b6675060209626b1c2d0))
- **plugin-nested-docs:** await populate breadcrumbs on resaveChildren ([#4226](https://github.com/payloadcms/payload/issues/4226)) ([4e41dd1](https://github.com/payloadcms/payload/commit/4e41dd1bf2706001fa03130adb1c69403795ac96))
- **richtext-lexical:** Blocks: Array row data is not removed ([#4209](https://github.com/payloadcms/payload/issues/4209)) ([0af9c4d](https://github.com/payloadcms/payload/commit/0af9c4d3985a6c46a071ef5ac28c8359cb320571))
- **richtext-lexical:** Blocks: fields without fulfilled condition are now skipped for validation ([50fab90](https://github.com/payloadcms/payload/commit/50fab902bd7baa1702ae0d995b4f58c1f5fca374))
- **richtext-lexical:** Blocks: make sure fields are wrapped in a uniquely-named group, change block node data format, fix react key error ([#3995](https://github.com/payloadcms/payload/issues/3995)) ([c068a87](https://github.com/payloadcms/payload/commit/c068a8784ec5780dbdca5416b25ba654afd05458))
- **richtext-lexical:** Blocks: z-index issue, e.g. select field dropdown in blocks hidden behind blocks below, or slash menu inside nested editor hidden behind blocks below ([09f17f4](https://github.com/payloadcms/payload/commit/09f17f44508539cfcb8722f7f462ef40d9ed54fd))
- **richtext-lexical:** Floating Select Toolbar: Buttons and Dropdown Buttons not clickable in nested editors ([615702b](https://github.com/payloadcms/payload/commit/615702b858e76994a174159cb69f034ef811e016)), closes [#4025](https://github.com/payloadcms/payload/issues/4025)
- **richtext-lexical:** incorrect caret positioning when selecting second line of multi-line paragraph ([#4165](https://github.com/payloadcms/payload/issues/4165)) ([b210af4](https://github.com/payloadcms/payload/commit/b210af46968b77d96ffd6ef60adc3b8d8bdc9376))
- **richtext-lexical:** make lexicalHTML() function work for globals ([dbfc835](https://github.com/payloadcms/payload/commit/dbfc83520ca8b5e55198a3c4b517ae3a80f9cac6))
- **richtext-lexical:** nested editor may lose focus when writing ([#4139](https://github.com/payloadcms/payload/issues/4139)) ([859c2f4](https://github.com/payloadcms/payload/commit/859c2f4a6d299a42e572133502b3841a74a11002))
- **richtext-lexical:** remove optional chaining after `this` as transpilers are not handling it well ([#4145](https://github.com/payloadcms/payload/issues/4145)) ([2c8d34d](https://github.com/payloadcms/payload/commit/2c8d34d2aadf2fcaf0655c0abef233f341d9945f))
- **richtext-lexical:** visual bug after rearranging blocks ([a6b4860](https://github.com/payloadcms/payload/commit/a6b486007dc26195adc5d576d937e35471c2868f))
- simplifies block/array/hasMany-number field validations ([#4052](https://github.com/payloadcms/payload/issues/4052)) ([803a37e](https://github.com/payloadcms/payload/commit/803a37eaa947397fa0a93b9f4f7d702c6b94ceaa))
- thread locale through to access routes from admin panel ([#4183](https://github.com/payloadcms/payload/issues/4183)) ([05f3169](https://github.com/payloadcms/payload/commit/05f3169a75b3b62962e7fe7842fbb6df6699433d))
- transactionID isolation for GraphQL ([#4095](https://github.com/payloadcms/payload/issues/4095)) ([195a952](https://github.com/payloadcms/payload/commit/195a952c4314e0d53fd579517035373b49d6ccae))
- upload fit not accounted for when editing focal point or crop ([#4142](https://github.com/payloadcms/payload/issues/4142)) ([45e9a55](https://github.com/payloadcms/payload/commit/45e9a559bbb16b2171465c8a439044011cebf102))
* conditionally hide dot menu in DocumentControls ([#4075](https://github.com/payloadcms/payload/issues/4075)) ([cef4cbb](https://github.com/payloadcms/payload/commit/cef4cbb0ee59e1b0b806808d79b402dce114755f))
* disable editing option for svg image types ([#4071](https://github.com/payloadcms/payload/issues/4071)) ([949e265](https://github.com/payloadcms/payload/commit/949e265cd9c95b7d4063336dde86177008d54839))
* fixes creation of related documents within a transaction if filterOptions is used ([#4087](https://github.com/payloadcms/payload/issues/4087)) ([acad288](https://github.com/payloadcms/payload/commit/acad2888cd9a13d5fb9e4c686b2267ea69454eaf))
* hide empty image sizes from the preview drawer ([#3946](https://github.com/payloadcms/payload/issues/3946)) ([687f485](https://github.com/payloadcms/payload/commit/687f4850acf073df0a649ef6182bfc8387857173))
* **live-preview:** ensures field schema exists before traversing fields ([#4074](https://github.com/payloadcms/payload/issues/4074)) ([7059a71](https://github.com/payloadcms/payload/commit/7059a71243a8f98dcc89af0bfe502247db9e4123))
* **live-preview:** field recursion and relationship population ([#4045](https://github.com/payloadcms/payload/issues/4045)) ([2ad7340](https://github.com/payloadcms/payload/commit/2ad73401546ef6608fd67d1f00b537f149640d6a))
* possible issue with access control not using req ([#4086](https://github.com/payloadcms/payload/issues/4086)) ([348a70c](https://github.com/payloadcms/payload/commit/348a70cc33409b0b48aff3acd2b94c2df5d88f3b))
* **richtext-lexical:** Blocks: unnecessary saving node value when initially opening a document & new lexical tests ([#4059](https://github.com/payloadcms/payload/issues/4059)) ([fff377a](https://github.com/payloadcms/payload/commit/fff377ad22cce3b26142cde8f4125fcee95aa072))
* **richtext-lexical:** floating select toolbar caret not positioned correctly if first line is selected ([#4062](https://github.com/payloadcms/payload/issues/4062)) ([c462df3](https://github.com/payloadcms/payload/commit/c462df38f65b155e131e6a7b46b2bb16cd090e45))
- conditionally hide dot menu in DocumentControls ([#4075](https://github.com/payloadcms/payload/issues/4075)) ([cef4cbb](https://github.com/payloadcms/payload/commit/cef4cbb0ee59e1b0b806808d79b402dce114755f))
- disable editing option for svg image types ([#4071](https://github.com/payloadcms/payload/issues/4071)) ([949e265](https://github.com/payloadcms/payload/commit/949e265cd9c95b7d4063336dde86177008d54839))
- fixes creation of related documents within a transaction if filterOptions is used ([#4087](https://github.com/payloadcms/payload/issues/4087)) ([acad288](https://github.com/payloadcms/payload/commit/acad2888cd9a13d5fb9e4c686b2267ea69454eaf))
- hide empty image sizes from the preview drawer ([#3946](https://github.com/payloadcms/payload/issues/3946)) ([687f485](https://github.com/payloadcms/payload/commit/687f4850acf073df0a649ef6182bfc8387857173))
- **live-preview:** ensures field schema exists before traversing fields ([#4074](https://github.com/payloadcms/payload/issues/4074)) ([7059a71](https://github.com/payloadcms/payload/commit/7059a71243a8f98dcc89af0bfe502247db9e4123))
- **live-preview:** field recursion and relationship population ([#4045](https://github.com/payloadcms/payload/issues/4045)) ([2ad7340](https://github.com/payloadcms/payload/commit/2ad73401546ef6608fd67d1f00b537f149640d6a))
- possible issue with access control not using req ([#4086](https://github.com/payloadcms/payload/issues/4086)) ([348a70c](https://github.com/payloadcms/payload/commit/348a70cc33409b0b48aff3acd2b94c2df5d88f3b))
- **richtext-lexical:** Blocks: unnecessary saving node value when initially opening a document & new lexical tests ([#4059](https://github.com/payloadcms/payload/issues/4059)) ([fff377a](https://github.com/payloadcms/payload/commit/fff377ad22cce3b26142cde8f4125fcee95aa072))
- **richtext-lexical:** floating select toolbar caret not positioned correctly if first line is selected ([#4062](https://github.com/payloadcms/payload/issues/4062)) ([c462df3](https://github.com/payloadcms/payload/commit/c462df38f65b155e131e6a7b46b2bb16cd090e45))
* add internationalization (i18n) to locales ([#4005](https://github.com/payloadcms/payload/issues/4005)) ([6a0a859](https://github.com/payloadcms/payload/commit/6a0a859563ed9e742260ea51a1839a1ef0f61fce))
* Custom Error, Label, and before/after field components ([#3747](https://github.com/payloadcms/payload/issues/3747)) ([266c327](https://github.com/payloadcms/payload/commit/266c3274d03e4fd52c692eeef1ee9248dcf66189))
- add internationalization (i18n) to locales ([#4005](https://github.com/payloadcms/payload/issues/4005)) ([6a0a859](https://github.com/payloadcms/payload/commit/6a0a859563ed9e742260ea51a1839a1ef0f61fce))
- Custom Error, Label, and before/after field components ([#3747](https://github.com/payloadcms/payload/issues/3747)) ([266c327](https://github.com/payloadcms/payload/commit/266c3274d03e4fd52c692eeef1ee9248dcf66189))
### Bug Fixes
* error on graphql multiple queries ([#3985](https://github.com/payloadcms/payload/issues/3985)) ([57da3c9](https://github.com/payloadcms/payload/commit/57da3c99a7e4ce5d3d1e17315e3691815f363704))
* focal and cropping issues, adds test ([#4039](https://github.com/payloadcms/payload/issues/4039)) ([acba5e4](https://github.com/payloadcms/payload/commit/acba5e482b7ddc6e3dc6ba9b7736022770d69a55))
* handle invalid tokens in refresh token operation ([#3647](https://github.com/payloadcms/payload/issues/3647)) ([131d89c](https://github.com/payloadcms/payload/commit/131d89c3f50c237e1ab2d7cd32d7a8226a9f8ce3))
* hasMany number and select fields unable to save within arrays ([#4047](https://github.com/payloadcms/payload/issues/4047)) ([182c57b](https://github.com/payloadcms/payload/commit/182c57b191010ce3dcf659f39c1dc2f7cf80662e))
* injects array and block ids into fieldSchemaToJSON ([#4043](https://github.com/payloadcms/payload/issues/4043)) ([d068ef7](https://github.com/payloadcms/payload/commit/d068ef7e2483d49dc41bdd7735042ddcaa0a684c))
* parse predefined migrations via file arg or name prefix ([#4001](https://github.com/payloadcms/payload/issues/4001)) ([eb42c03](https://github.com/payloadcms/payload/commit/eb42c031ef980558ed051d4163925aa28d6ab090))
* polymorphic hasMany relationships missing in postgres admin ([#4053](https://github.com/payloadcms/payload/issues/4053)) ([7a9af44](https://github.com/payloadcms/payload/commit/7a9af4417a56c621f01195f9a2904b9adffaad7a))
* resets list filter row when the filter on field is changed ([#3956](https://github.com/payloadcms/payload/issues/3956)) ([8d14c21](https://github.com/payloadcms/payload/commit/8d14c213c878a1afda2b3bf03431fed5aa2a44e3))
* Update API Views ([b008b6c](https://github.com/payloadcms/payload/commit/b008b6c6463c9dc3d8e61eaa0a9210aa1a189442))
* vite not replacing env vars correctly when building ([67b3baa](https://github.com/payloadcms/payload/commit/67b3baaa445a13246be8178d57eaeba92888bef1))
- error on graphql multiple queries ([#3985](https://github.com/payloadcms/payload/issues/3985)) ([57da3c9](https://github.com/payloadcms/payload/commit/57da3c99a7e4ce5d3d1e17315e3691815f363704))
- focal and cropping issues, adds test ([#4039](https://github.com/payloadcms/payload/issues/4039)) ([acba5e4](https://github.com/payloadcms/payload/commit/acba5e482b7ddc6e3dc6ba9b7736022770d69a55))
- handle invalid tokens in refresh token operation ([#3647](https://github.com/payloadcms/payload/issues/3647)) ([131d89c](https://github.com/payloadcms/payload/commit/131d89c3f50c237e1ab2d7cd32d7a8226a9f8ce3))
- hasMany number and select fields unable to save within arrays ([#4047](https://github.com/payloadcms/payload/issues/4047)) ([182c57b](https://github.com/payloadcms/payload/commit/182c57b191010ce3dcf659f39c1dc2f7cf80662e))
- injects array and block ids into fieldSchemaToJSON ([#4043](https://github.com/payloadcms/payload/issues/4043)) ([d068ef7](https://github.com/payloadcms/payload/commit/d068ef7e2483d49dc41bdd7735042ddcaa0a684c))
- parse predefined migrations via file arg or name prefix ([#4001](https://github.com/payloadcms/payload/issues/4001)) ([eb42c03](https://github.com/payloadcms/payload/commit/eb42c031ef980558ed051d4163925aa28d6ab090))
- polymorphic hasMany relationships missing in postgres admin ([#4053](https://github.com/payloadcms/payload/issues/4053)) ([7a9af44](https://github.com/payloadcms/payload/commit/7a9af4417a56c621f01195f9a2904b9adffaad7a))
- resets list filter row when the filter on field is changed ([#3956](https://github.com/payloadcms/payload/issues/3956)) ([8d14c21](https://github.com/payloadcms/payload/commit/8d14c213c878a1afda2b3bf03431fed5aa2a44e3))
- Update API Views ([b008b6c](https://github.com/payloadcms/payload/commit/b008b6c6463c9dc3d8e61eaa0a9210aa1a189442))
- vite not replacing env vars correctly when building ([67b3baa](https://github.com/payloadcms/payload/commit/67b3baaa445a13246be8178d57eaeba92888bef1))
* do not display field if read permission is false - admin panel ui ([#3949](https://github.com/payloadcms/payload/issues/3949)) ([cdc10be](https://github.com/payloadcms/payload/commit/cdc10be1a241c6a9ac09feab77bcd58d23ff3dd9))
* ensures dataloader does not run requests in parallel ([4607dbf](https://github.com/payloadcms/payload/commit/4607dbf97694bc899e597e9c7df50b6c878874f5))
* exclude files from dev bundle if aliased ([#3957](https://github.com/payloadcms/payload/issues/3957)) ([7966692](https://github.com/payloadcms/payload/commit/796669279afb8fe23723ce36e6e47a44b7088b09))
* field paths being mutated if they ended with the req.locale ([#3936](https://github.com/payloadcms/payload/issues/3936)) ([36576f1](https://github.com/payloadcms/payload/commit/36576f152ace41edd8b353703db2598d04deae44))
* passes correct data to buildStateFromSchema on account page ([#3984](https://github.com/payloadcms/payload/issues/3984)) ([c7a315a](https://github.com/payloadcms/payload/commit/c7a315a7d1075361a7ee432a449769397c12185e))
* prevent sort from saving a new version in version list view ([#3944](https://github.com/payloadcms/payload/issues/3944)) ([900a9ea](https://github.com/payloadcms/payload/commit/900a9eafeb51b1e5130518d4f71034a2bf9e4c5b))
* properly load temp files into buffer ([#3996](https://github.com/payloadcms/payload/issues/3996)) ([d1a0822](https://github.com/payloadcms/payload/commit/d1a0822f8044a3f65416f4fe608e91a4ceea6b56))
* sort document tabs by order ([#3968](https://github.com/payloadcms/payload/issues/3968)) ([06cd52b](https://github.com/payloadcms/payload/commit/06cd52b622723503896af6262907d31b258d0a5e))
* vertical alignment in step nav when using larger logos ([#3955](https://github.com/payloadcms/payload/issues/3955)) ([b6d9a20](https://github.com/payloadcms/payload/commit/b6d9a2021fafea594353329fd304553bf7f2d091))
- autosave updating data in unrelated docs ([b722f20](https://github.com/payloadcms/payload/commit/b722f202af39a1429298b700cac686ecbbd4b46b))
- better error handling within parseCookies ([#3720](https://github.com/payloadcms/payload/issues/3720)) ([6b1b4ff](https://github.com/payloadcms/payload/commit/6b1b4ffd27cc9a84e22ef2f3a8e389e5b72d41bc))
- do not display field if read permission is false - admin panel ui ([#3949](https://github.com/payloadcms/payload/issues/3949)) ([cdc10be](https://github.com/payloadcms/payload/commit/cdc10be1a241c6a9ac09feab77bcd58d23ff3dd9))
- ensures dataloader does not run requests in parallel ([4607dbf](https://github.com/payloadcms/payload/commit/4607dbf97694bc899e597e9c7df50b6c878874f5))
- exclude files from dev bundle if aliased ([#3957](https://github.com/payloadcms/payload/issues/3957)) ([7966692](https://github.com/payloadcms/payload/commit/796669279afb8fe23723ce36e6e47a44b7088b09))
- field paths being mutated if they ended with the req.locale ([#3936](https://github.com/payloadcms/payload/issues/3936)) ([36576f1](https://github.com/payloadcms/payload/commit/36576f152ace41edd8b353703db2598d04deae44))
- passes correct data to buildStateFromSchema on account page ([#3984](https://github.com/payloadcms/payload/issues/3984)) ([c7a315a](https://github.com/payloadcms/payload/commit/c7a315a7d1075361a7ee432a449769397c12185e))
- prevent sort from saving a new version in version list view ([#3944](https://github.com/payloadcms/payload/issues/3944)) ([900a9ea](https://github.com/payloadcms/payload/commit/900a9eafeb51b1e5130518d4f71034a2bf9e4c5b))
- properly load temp files into buffer ([#3996](https://github.com/payloadcms/payload/issues/3996)) ([d1a0822](https://github.com/payloadcms/payload/commit/d1a0822f8044a3f65416f4fe608e91a4ceea6b56))
- sort document tabs by order ([#3968](https://github.com/payloadcms/payload/issues/3968)) ([06cd52b](https://github.com/payloadcms/payload/commit/06cd52b622723503896af6262907d31b258d0a5e))
- vertical alignment in step nav when using larger logos ([#3955](https://github.com/payloadcms/payload/issues/3955)) ([b6d9a20](https://github.com/payloadcms/payload/commit/b6d9a2021fafea594353329fd304553bf7f2d091))
* adds null to non-required field unions ([#3870](https://github.com/payloadcms/payload/issues/3870)) ([7e919aa](https://github.com/payloadcms/payload/commit/7e919aa87c0116c41bf41d75dcd91ff96576a46f))
* checks for user before accessing properties in preferences update operation([#3844](https://github.com/payloadcms/payload/issues/3844)) ([24eab3a](https://github.com/payloadcms/payload/commit/24eab3af1da3b08debe0580cce8ece08a86039a4))
* disable webpack hot reload on production ([#3891](https://github.com/payloadcms/payload/issues/3891)) ([422c803](https://github.com/payloadcms/payload/commit/422c803da67982a063a028508c44009b9577646e))
* duplicate document copying to incorrect locale ([#3874](https://github.com/payloadcms/payload/issues/3874)) ([89f273b](https://github.com/payloadcms/payload/commit/89f273bf894512b69e8647be4cf4496bff37c99b))
* ensure serverURL has string value for getBaseUploadFields function ([#3900](https://github.com/payloadcms/payload/issues/3900)) ([c564a83](https://github.com/payloadcms/payload/commit/c564a83ab61b672d9a2bcc875ab890b0fede5462))
* ensures compare-version select field cannot be cleared ([#3901](https://github.com/payloadcms/payload/issues/3901)) ([42d8d11](https://github.com/payloadcms/payload/commit/42d8d11fd7e5792b119f69f17dc1100c85cfa491))
* error handling when duplicating documents fails ([#3873](https://github.com/payloadcms/payload/issues/3873)) ([435eb62](https://github.com/payloadcms/payload/commit/435eb6204e550e898a81033f794fcf568e3b017c))
* generate new block ids on create ([#3871](https://github.com/payloadcms/payload/issues/3871)) ([3404bab](https://github.com/payloadcms/payload/commit/3404bab83f1112713675eb504870a4a1786c3822))
* global permissions for live preview ([#3854](https://github.com/payloadcms/payload/issues/3854)) ([3032e0b](https://github.com/payloadcms/payload/commit/3032e0b5a239db0762abd120b2db95f30ed5ca65))
* handles null & undefined relationship field values in versions view ([#3609](https://github.com/payloadcms/payload/issues/3609)) ([115e592](https://github.com/payloadcms/payload/commit/115e592b54d9174f316daa3cff31bcc801eaf92f))
* incorrect duplication of data in admin ui ([#3907](https://github.com/payloadcms/payload/issues/3907)) ([46fc41c](https://github.com/payloadcms/payload/commit/46fc41cbd9615c58248b4d2c44d24905dd676171))
* only apply focal manipulation when necessary ([#3902](https://github.com/payloadcms/payload/issues/3902)) ([a4f36aa](https://github.com/payloadcms/payload/commit/a4f36aa8a009e9c0156924320bbcf1d04b697223))
* removes conditional return of formattedEmails in sendEmail hook [#26](https://github.com/payloadcms/payload/issues/26) ([#28](https://github.com/payloadcms/payload/issues/28)) ([e8458f8](https://github.com/payloadcms/payload/commit/e8458f84bcd5bad74b189479931fbb7faea74900))
* resize image if no aspect ratio change ([#3859](https://github.com/payloadcms/payload/issues/3859)) ([f53b713](https://github.com/payloadcms/payload/commit/f53b7131548dbe9071c65ba110f7f0d206bb33b5))
* **richtext-*:** type issues with typescript strict mode enabled ([dac9514](https://github.com/payloadcms/payload/commit/dac9514eb00b99a3caeb9f217695b2b89368f7c9))
* **richtext-lexical:** Blocks node incorrectly marked as client module ([35f00fa](https://github.com/payloadcms/payload/commit/35f00fa83d2a90967e0707ca0fd960c5608a3bf3))
* set date to 12UTC for default, dayOnly and monthOnly fields ([#3887](https://github.com/payloadcms/payload/issues/3887)) ([d393225](https://github.com/payloadcms/payload/commit/d3932252891bb8721a5abc97e204dbb6a7f3fda2))
* store resized image on req or tempFilePath ([#3883](https://github.com/payloadcms/payload/issues/3883)) ([6c5d525](https://github.com/payloadcms/payload/commit/6c5d525d8e1267eebdffeb9f31b2ef3a4df1132d))
* unique field error handling ([#3888](https://github.com/payloadcms/payload/issues/3888)) ([4d8d4c2](https://github.com/payloadcms/payload/commit/4d8d4c214ab12571e9dc71e406117c4b19f63c6b))
- adds null to non-required field unions ([#3870](https://github.com/payloadcms/payload/issues/3870)) ([7e919aa](https://github.com/payloadcms/payload/commit/7e919aa87c0116c41bf41d75dcd91ff96576a46f))
- checks for user before accessing properties in preferences update operation([#3844](https://github.com/payloadcms/payload/issues/3844)) ([24eab3a](https://github.com/payloadcms/payload/commit/24eab3af1da3b08debe0580cce8ece08a86039a4))
- disable webpack hot reload on production ([#3891](https://github.com/payloadcms/payload/issues/3891)) ([422c803](https://github.com/payloadcms/payload/commit/422c803da67982a063a028508c44009b9577646e))
- duplicate document copying to incorrect locale ([#3874](https://github.com/payloadcms/payload/issues/3874)) ([89f273b](https://github.com/payloadcms/payload/commit/89f273bf894512b69e8647be4cf4496bff37c99b))
- ensure serverURL has string value for getBaseUploadFields function ([#3900](https://github.com/payloadcms/payload/issues/3900)) ([c564a83](https://github.com/payloadcms/payload/commit/c564a83ab61b672d9a2bcc875ab890b0fede5462))
- ensures compare-version select field cannot be cleared ([#3901](https://github.com/payloadcms/payload/issues/3901)) ([42d8d11](https://github.com/payloadcms/payload/commit/42d8d11fd7e5792b119f69f17dc1100c85cfa491))
- error handling when duplicating documents fails ([#3873](https://github.com/payloadcms/payload/issues/3873)) ([435eb62](https://github.com/payloadcms/payload/commit/435eb6204e550e898a81033f794fcf568e3b017c))
- generate new block ids on create ([#3871](https://github.com/payloadcms/payload/issues/3871)) ([3404bab](https://github.com/payloadcms/payload/commit/3404bab83f1112713675eb504870a4a1786c3822))
- global permissions for live preview ([#3854](https://github.com/payloadcms/payload/issues/3854)) ([3032e0b](https://github.com/payloadcms/payload/commit/3032e0b5a239db0762abd120b2db95f30ed5ca65))
- handles null & undefined relationship field values in versions view ([#3609](https://github.com/payloadcms/payload/issues/3609)) ([115e592](https://github.com/payloadcms/payload/commit/115e592b54d9174f316daa3cff31bcc801eaf92f))
- incorrect duplication of data in admin ui ([#3907](https://github.com/payloadcms/payload/issues/3907)) ([46fc41c](https://github.com/payloadcms/payload/commit/46fc41cbd9615c58248b4d2c44d24905dd676171))
- only apply focal manipulation when necessary ([#3902](https://github.com/payloadcms/payload/issues/3902)) ([a4f36aa](https://github.com/payloadcms/payload/commit/a4f36aa8a009e9c0156924320bbcf1d04b697223))
- removes conditional return of formattedEmails in sendEmail hook [#26](https://github.com/payloadcms/payload/issues/26) ([#28](https://github.com/payloadcms/payload/issues/28)) ([e8458f8](https://github.com/payloadcms/payload/commit/e8458f84bcd5bad74b189479931fbb7faea74900))
- resize image if no aspect ratio change ([#3859](https://github.com/payloadcms/payload/issues/3859)) ([f53b713](https://github.com/payloadcms/payload/commit/f53b7131548dbe9071c65ba110f7f0d206bb33b5))
- **richtext-\*:** type issues with typescript strict mode enabled ([dac9514](https://github.com/payloadcms/payload/commit/dac9514eb00b99a3caeb9f217695b2b89368f7c9))
- **richtext-lexical:** Blocks node incorrectly marked as client module ([35f00fa](https://github.com/payloadcms/payload/commit/35f00fa83d2a90967e0707ca0fd960c5608a3bf3))
- set date to 12UTC for default, dayOnly and monthOnly fields ([#3887](https://github.com/payloadcms/payload/issues/3887)) ([d393225](https://github.com/payloadcms/payload/commit/d3932252891bb8721a5abc97e204dbb6a7f3fda2))
- store resized image on req or tempFilePath ([#3883](https://github.com/payloadcms/payload/issues/3883)) ([6c5d525](https://github.com/payloadcms/payload/commit/6c5d525d8e1267eebdffeb9f31b2ef3a4df1132d))
- unique field error handling ([#3888](https://github.com/payloadcms/payload/issues/3888)) ([4d8d4c2](https://github.com/payloadcms/payload/commit/4d8d4c214ab12571e9dc71e406117c4b19f63c6b))
* adjusts props to accept components for before and after fields instead of functions ([#3820](https://github.com/payloadcms/payload/issues/3820)) ([c476d01](https://github.com/payloadcms/payload/commit/c476d01f4e5016f9c2bc338103ef2c778139a536))
* alignment of collapsible within row ([#3822](https://github.com/payloadcms/payload/issues/3822)) ([eaef0e7](https://github.com/payloadcms/payload/commit/eaef0e739546b4411d971da21170977ba73695f8))
* named tabs not appearing in the gql mutation input type ([#3835](https://github.com/payloadcms/payload/issues/3835)) ([a0019d0](https://github.com/payloadcms/payload/commit/a0019d0a78504b5c4d6aeec4823d7a0e224f1d6b))
* only parses live preview ready message when same origin ([#3791](https://github.com/payloadcms/payload/issues/3791)) ([e8f2377](https://github.com/payloadcms/payload/commit/e8f237783b9f48edf80b1d8c61142aeb2edb1c0b))
* prevent storing duplicate user preferences ([#3833](https://github.com/payloadcms/payload/issues/3833)) ([7eee0ec](https://github.com/payloadcms/payload/commit/7eee0ec3558c8b65afc38df7377073f042402ee3))
* prevents document sidebar from collapsing ([71a3e5b](https://github.com/payloadcms/payload/commit/71a3e5ba1037fe447dccad4a490fdfb1623ba0b0))
* renders live preview for globals ([#3801](https://github.com/payloadcms/payload/issues/3801)) ([a13ec2e](https://github.com/payloadcms/payload/commit/a13ec2ebc4858029c643f4530daa4ed49a7b024e))
- adjusts props to accept components for before and after fields instead of functions ([#3820](https://github.com/payloadcms/payload/issues/3820)) ([c476d01](https://github.com/payloadcms/payload/commit/c476d01f4e5016f9c2bc338103ef2c778139a536))
- alignment of collapsible within row ([#3822](https://github.com/payloadcms/payload/issues/3822)) ([eaef0e7](https://github.com/payloadcms/payload/commit/eaef0e739546b4411d971da21170977ba73695f8))
- named tabs not appearing in the gql mutation input type ([#3835](https://github.com/payloadcms/payload/issues/3835)) ([a0019d0](https://github.com/payloadcms/payload/commit/a0019d0a78504b5c4d6aeec4823d7a0e224f1d6b))
- only parses live preview ready message when same origin ([#3791](https://github.com/payloadcms/payload/issues/3791)) ([e8f2377](https://github.com/payloadcms/payload/commit/e8f237783b9f48edf80b1d8c61142aeb2edb1c0b))
- prevent storing duplicate user preferences ([#3833](https://github.com/payloadcms/payload/issues/3833)) ([7eee0ec](https://github.com/payloadcms/payload/commit/7eee0ec3558c8b65afc38df7377073f042402ee3))
- prevents document sidebar from collapsing ([71a3e5b](https://github.com/payloadcms/payload/commit/71a3e5ba1037fe447dccad4a490fdfb1623ba0b0))
- renders live preview for globals ([#3801](https://github.com/payloadcms/payload/issues/3801)) ([a13ec2e](https://github.com/payloadcms/payload/commit/a13ec2ebc4858029c643f4530daa4ed49a7b024e))
* collection, global and field props for hooks, fix request context initialization, add context to global hooks ([#3780](https://github.com/payloadcms/payload/pull/3780))
* **richtext-lexical:** HTML Serializer ([#3685](https://github.com/payloadcms/payload/pull/3685))
- collection, global and field props for hooks, fix request context initialization, add context to global hooks ([#3780](https://github.com/payloadcms/payload/pull/3780))
- **richtext-lexical:** HTML Serializer ([#3685](https://github.com/payloadcms/payload/pull/3685))
### Bug Fixes
* remove duplicate removal of temp upload file ([#3818](https://github.com/payloadcms/payload/pull/3818))
* simplify how the search input and query params are connected ([#3797](https://github.com/payloadcms/payload/pull/3797))
* standardizes layout of document fields ([#3798](https://github.com/payloadcms/payload/pull/3798))
* issue where dragging unsortable item would crash the page ([#3789](https://github.com/payloadcms/payload/pull/3789))
* **richtext-lexical:** defaultValue property didn't fit into field schema ([b5c7bbed9](https://github.com/payloadcms/payload/commit/b5c7bbed93b532ec54a9c73537f4cb1290122a66))
* **richtext-*:** hasMany relationships not populated correctly ([e197e0316](https://github.com/payloadcms/payload/commit/e197e0316f9c01f945dc7f6d21ac28f9f0420f1d))
- remove duplicate removal of temp upload file ([#3818](https://github.com/payloadcms/payload/pull/3818))
- simplify how the search input and query params are connected ([#3797](https://github.com/payloadcms/payload/pull/3797))
- standardizes layout of document fields ([#3798](https://github.com/payloadcms/payload/pull/3798))
- issue where dragging unsortable item would crash the page ([#3789](https://github.com/payloadcms/payload/pull/3789))
- **richtext-lexical:** defaultValue property didn't fit into field schema ([b5c7bbed9](https://github.com/payloadcms/payload/commit/b5c7bbed93b532ec54a9c73537f4cb1290122a66))
- **richtext-\*:** hasMany relationships not populated correctly ([e197e0316](https://github.com/payloadcms/payload/commit/e197e0316f9c01f945dc7f6d21ac28f9f0420f1d))
* filesRequired is optional for uploads ([#3668](https://github.com/payloadcms/payload/pull/3668)) ([48de897](https://github.com/payloadcms/payload/commit/48de89794b2c5d94183090b0830fd355d8d6c6f3))
- filesRequired is optional for uploads ([#3668](https://github.com/payloadcms/payload/pull/3668)) ([48de897](https://github.com/payloadcms/payload/commit/48de89794b2c5d94183090b0830fd355d8d6c6f3))
### Bug Fixes
* Register first user verify update missing transaction id / req ([#3665](https://github.com/payloadcms/payload/pull/3665)) ([68c5a5751](https://github.com/payloadcms/payload/commit/68c5a57515ffbba37c9194a75d0f672bdb10d96b))
- Register first user verify update missing transaction id / req ([#3665](https://github.com/payloadcms/payload/pull/3665)) ([68c5a5751](https://github.com/payloadcms/payload/commit/68c5a57515ffbba37c9194a75d0f672bdb10d96b))
* allows filterOptions to return null ([c4cac99](https://github.com/payloadcms/payload/commit/c4cac998752730e7084598c92c77789da8c82e0d))
* **live-preview:** caches field schema ([#3711](https://github.com/payloadcms/payload/issues/3711)) ([dd0ac06](https://github.com/payloadcms/payload/commit/dd0ac066ce2ed88b85025309303610a95b6089e1))
- allows filterOptions to return null ([c4cac99](https://github.com/payloadcms/payload/commit/c4cac998752730e7084598c92c77789da8c82e0d))
- **live-preview:** caches field schema ([#3711](https://github.com/payloadcms/payload/issues/3711)) ([dd0ac06](https://github.com/payloadcms/payload/commit/dd0ac066ce2ed88b85025309303610a95b6089e1))
### Bug Fixes
* autosave time shown minutes only ([#3492](https://github.com/payloadcms/payload/issues/3492)) ([e311e8f](https://github.com/payloadcms/payload/commit/e311e8fff9cd4264d7a71903f63c4fa825a3564d))
* blocks within groups in postgres ([45a62ba](https://github.com/payloadcms/payload/commit/45a62ba949aca33b25e0808773a5c2f1cf4adf82))
* bug with seeding ecommerce ([993568a](https://github.com/payloadcms/payload/commit/993568a1959ea10f960e35e4ed7a8e06af672a72))
* corrects add block index ([#3681](https://github.com/payloadcms/payload/issues/3681)) ([3c50443](https://github.com/payloadcms/payload/commit/3c5044368d5b30c76a2ff20c25b9234ef89dc205))
* misc upload crop/focal point updates ([#3580](https://github.com/payloadcms/payload/issues/3580)) ([d616772](https://github.com/payloadcms/payload/commit/d6167727401a01282345e63636560e029ae8e0f3))
* renders mobile document controls ([#3695](https://github.com/payloadcms/payload/issues/3695)) ([1625ff2](https://github.com/payloadcms/payload/commit/1625ff244e6e81e6edc0357037c3abc1a3bf8ba7))
* some local operations missing req.transactionID ([#3651](https://github.com/payloadcms/payload/issues/3651)) ([150799e](https://github.com/payloadcms/payload/commit/150799e10e580281d1af49388eb142ee9639a002))
* **richtext-*:** extra fields not being iterated correctly ([#3693](https://github.com/payloadcms/payload/issues/3693)) ([b8a5866](https://github.com/payloadcms/payload/commit/b8a58666e70f604af1e1cf349bcb4f9add0147e7))
* **richtext-*:** link drawer form receiving incorrect field schema ([#3696](https://github.com/payloadcms/payload/issues/3696)) ([cb39354](https://github.com/payloadcms/payload/commit/cb39354a9de3d20960110e453f62c4aa166d8448))
* **templates:** user access control ([#3712](https://github.com/payloadcms/payload/issues/3712)) ([8b8ceab](https://github.com/payloadcms/payload/commit/8b8ceabbdd6354761e7d744cacb1192cac3a2427))
- autosave time shown minutes only ([#3492](https://github.com/payloadcms/payload/issues/3492)) ([e311e8f](https://github.com/payloadcms/payload/commit/e311e8fff9cd4264d7a71903f63c4fa825a3564d))
- blocks within groups in postgres ([45a62ba](https://github.com/payloadcms/payload/commit/45a62ba949aca33b25e0808773a5c2f1cf4adf82))
- bug with seeding ecommerce ([993568a](https://github.com/payloadcms/payload/commit/993568a1959ea10f960e35e4ed7a8e06af672a72))
- corrects add block index ([#3681](https://github.com/payloadcms/payload/issues/3681)) ([3c50443](https://github.com/payloadcms/payload/commit/3c5044368d5b30c76a2ff20c25b9234ef89dc205))
- misc upload crop/focal point updates ([#3580](https://github.com/payloadcms/payload/issues/3580)) ([d616772](https://github.com/payloadcms/payload/commit/d6167727401a01282345e63636560e029ae8e0f3))
- renders mobile document controls ([#3695](https://github.com/payloadcms/payload/issues/3695)) ([1625ff2](https://github.com/payloadcms/payload/commit/1625ff244e6e81e6edc0357037c3abc1a3bf8ba7))
- some local operations missing req.transactionID ([#3651](https://github.com/payloadcms/payload/issues/3651)) ([150799e](https://github.com/payloadcms/payload/commit/150799e10e580281d1af49388eb142ee9639a002))
- **richtext-\*:** extra fields not being iterated correctly ([#3693](https://github.com/payloadcms/payload/issues/3693)) ([b8a5866](https://github.com/payloadcms/payload/commit/b8a58666e70f604af1e1cf349bcb4f9add0147e7))
- **richtext-\*:** link drawer form receiving incorrect field schema ([#3696](https://github.com/payloadcms/payload/issues/3696)) ([cb39354](https://github.com/payloadcms/payload/commit/cb39354a9de3d20960110e453f62c4aa166d8448))
- **templates:** user access control ([#3712](https://github.com/payloadcms/payload/issues/3712)) ([8b8ceab](https://github.com/payloadcms/payload/commit/8b8ceabbdd6354761e7d744cacb1192cac3a2427))
* login form clearing out and field spacing ([#3633](https://github.com/payloadcms/payload/issues/3633)) ([4bd01df](https://github.com/payloadcms/payload/commit/4bd01df411e4ad2ccacdcd6de0fb21a8145c3964))
* sidebar field permissions ([#3629](https://github.com/payloadcms/payload/issues/3629)) ([c956a85](https://github.com/payloadcms/payload/commit/c956a85252bc7de1686925cc783694383c0ac9be))
- login form clearing out and field spacing ([#3633](https://github.com/payloadcms/payload/issues/3633)) ([4bd01df](https://github.com/payloadcms/payload/commit/4bd01df411e4ad2ccacdcd6de0fb21a8145c3964))
- sidebar field permissions ([#3629](https://github.com/payloadcms/payload/issues/3629)) ([c956a85](https://github.com/payloadcms/payload/commit/c956a85252bc7de1686925cc783694383c0ac9be))
* properly renders custom buttons for globals ([#3616](https://github.com/payloadcms/payload/issues/3616)) ([05cc287](https://github.com/payloadcms/payload/commit/05cc2873b4a19e2bd46be778f3610643d3e15d09))
* minor type issue in richText validate function ([06a51b3](https://github.com/payloadcms/payload/commit/06a51b3c9b9045b23051807aa03b222b542b46f5))
* live preview device size ([#3606](https://github.com/payloadcms/payload/issues/3606)) ([8bbac60](https://github.com/payloadcms/payload/commit/8bbac60e60a6dbe4dc0c7b05edbca7f6f2d1c569))
* properly handles nested routes for live preview ([#3586](https://github.com/payloadcms/payload/issues/3586)) ([6486468](https://github.com/payloadcms/payload/commit/64864686c418f9822bf61c45ece078a39e81b4cb))
* various stepnav related issues ([#3599](https://github.com/payloadcms/payload/issues/3599)) ([aaf8839](https://github.com/payloadcms/payload/commit/aaf883909c588bae1145ddddc5291a98740c2c03))
* postgres select fields within groups ([#3570](https://github.com/payloadcms/payload/issues/3570)) ([06e2fa9](https://github.com/payloadcms/payload/commit/06e2fa9d111c18fad3422953082266db9329fc91))
* [#3511](https://github.com/payloadcms/payload/issues/3511), documents don't delete their versions ([#3520](https://github.com/payloadcms/payload/issues/3520)) ([e3c7765](https://github.com/payloadcms/payload/commit/e3c776523a64da03a4756ee5ae8a84175090979f))
- properly renders custom buttons for globals ([#3616](https://github.com/payloadcms/payload/issues/3616)) ([05cc287](https://github.com/payloadcms/payload/commit/05cc2873b4a19e2bd46be778f3610643d3e15d09))
- minor type issue in richText validate function ([06a51b3](https://github.com/payloadcms/payload/commit/06a51b3c9b9045b23051807aa03b222b542b46f5))
- live preview device size ([#3606](https://github.com/payloadcms/payload/issues/3606)) ([8bbac60](https://github.com/payloadcms/payload/commit/8bbac60e60a6dbe4dc0c7b05edbca7f6f2d1c569))
- properly handles nested routes for live preview ([#3586](https://github.com/payloadcms/payload/issues/3586)) ([6486468](https://github.com/payloadcms/payload/commit/64864686c418f9822bf61c45ece078a39e81b4cb))
- various stepnav related issues ([#3599](https://github.com/payloadcms/payload/issues/3599)) ([aaf8839](https://github.com/payloadcms/payload/commit/aaf883909c588bae1145ddddc5291a98740c2c03))
- postgres select fields within groups ([#3570](https://github.com/payloadcms/payload/issues/3570)) ([06e2fa9](https://github.com/payloadcms/payload/commit/06e2fa9d111c18fad3422953082266db9329fc91))
- [#3511](https://github.com/payloadcms/payload/issues/3511), documents don't delete their versions ([#3520](https://github.com/payloadcms/payload/issues/3520)) ([e3c7765](https://github.com/payloadcms/payload/commit/e3c776523a64da03a4756ee5ae8a84175090979f))
These new properties are all now required for Payload to function, and you will have to install each separate adapter that you use. Feel free to swap out any of the adapters with your choice (Lexical, Postgres, Vite, etc.)
@@ -63,7 +63,7 @@ Each test directory is split up in this way specifically to reduce friction when
The following command will start Payload with your config: `pnpm dev my-test-dir`. Example: `pnpm dev fields` for the test/`fields` test suite. This command will start up Payload using your config and refresh a test database on every restart. If you're using VS Code, the most common run configs are automatically added to your editor - you should be able to find them in your VS Code launch tab.
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/config#admin-autologin) with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `pnpm dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/overview#admin-autologin) with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `pnpm dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password. These are used in the auto-login.
@@ -120,5 +120,5 @@ This is how you can preview changes you made locally to the docs:
2. Run `yarn install`
3. Duplicate the `.env.example` file and rename it to `.env`
4. Add a `DOCS_DIR` environment variable to the `.env` file which points to the absolute path of your modified docs folder. For example `DOCS_DIR=/Users/yourname/Documents/GitHub/payload/docs`
5. Run `yarn run fetchDocs:local`. If this was successful, you should see no error messages and the following output: *Docs successfully written to /.../website/src/app/docs.json*. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
5. Run `yarn run fetchDocs:local`. If this was successful, you should see no error messages and the following output: _Docs successfully written to /.../website/src/app/docs.json_. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
6. You're done! Now you can start the website locally using `yarn run dev` and preview the docs under [http://localhost:3000/docs/](http://localhost:3000/docs/)
You can define Collection-level Access Control within each Collection's `access` property. All Access Control functions accept one `args` argument.
Collection Access Control is [Access Control](../access-control) used to restrict access to Documents within a [Collection](../collections/overview), as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Collection.
## Available Controls
To add Access Control to a Collection, use the `access` property in your [Collection Config](../configuration/collections):
| **`readVersions`** | Used to control who can read versions, and who can't. Will automatically restrict the Admin UI version viewing access. [More details](#read-versions). |
### Create
Returns a boolean which allows/denies access to the `create` request.
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
| **`data`** | The data passed to create the document with. |
### Read
Read access functions can return a boolean result or optionally return a [query constraint](/docs/queries/overview) which limits the documents that are returned to only those that match the constraint you provide. This can be helpful to restrict users' access to only certain documents however you specify.
Returns a boolean which allows/denies access to the `read` request.
Return a [Query](../queries/overview) to limit the Documents to only those that match the constraint. This can be helpful to restrict users' access to specific Documents. [More details](../queries/overview).
</Banner>
As your application becomes more complex, you may want to define your function in a separate file and import them into your Collection Config:
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
| **`id`** | `id` of document requested, if within `findByID`. |
### Update
Update access functions can return a boolean result or optionally return a [query constraint](/docs/queries/overview) to limit the document(s) that can be updated by the currently authenticated user. For example, returning a `query` from the `update` Access Control is helpful in cases where you would like to restrict a user to only being able to update the documents containing a `createdBy` relationship field equal to the user's ID.
Returns a boolean which allows/denies access to the `update` request.
Return a [Query](../queries/overview) to limit the Documents to only those that match the constraint. This can be helpful to restrict users' access to specific Documents. [More details](../queries/overview).
</Banner>
As your application becomes more complex, you may want to define your function in a separate file and import them into your Collection Config:
```ts
import type { Access } from 'payload'
export const canUpdateUser: Access = ({ req: { user }, id }) => {
// Allow users with a role of 'admin'
if (user.roles && user.roles.some((role) => role === 'admin')) {
return true
}
// allow any other users to update only oneself
return user.id === id
}
```
The following arguments are provided to the `update` function:
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
| **`id`** | `id` of document requested to update. |
| **`data`** | The data passed to update the document with. |
### Delete
Similarly to the Update function, returns a boolean or a [query constraint](/docs/queries/overview) to limit which documents can be deleted by which users.
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object with additional `user` property, which is the currently logged in user. |
| **`id`** | `id` of document requested to delete.
### Admin
If the Collection is [used to access the Payload Admin panel](/docs/admin/overview#the-admin-user-collection), the `Admin` Access Control function determines whether or not the currently logged in user can access the admin UI.
If the Collection is use to access the [Admin Panel](../admin/overview#the-admin-user-collection), the `Admin` Access Control function determines whether or not the currently logged in user can access the admin UI.
**Available argument properties:**
To add Admin Access Control to a Collection, use the `admin` property in the [Collection Config](../collections/overview):
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
### Unlock
Determines which users can [unlock](/docs/authentication/operations#unlock) other users who may be blocked from authenticating successfully due to [failing too many login attempts](/docs/authentication/config#options).
Determines which users can [unlock](/docs/authentication/operations#unlock) other users who may be blocked from authenticating successfully due to [failing too many login attempts](/docs/authentication/overview#options).
**Available argument properties:**
To add Unlock Access Control to a Collection, use the `unlock` property in the [Collection Config](../collections/overview):
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
### Read Versions
If the Collection has [Versions](../versions/overview) enabled, the `readVersions` Access Control function determines whether or not the currently logged in user can access the version history of a Document.
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Collection Config](../collections/overview):
Field Access Control is specified with functions inside a field's config. All field-level Controls return a boolean value to allow or deny access for the specified operation. No field-level Access Controls support returning query constraints. All Access Control functions accept one `args` argument.
Field Access Control is [Access Control](../access-control) used to restrict access to specific [Fields](../fields/overview) within a Document.
| **`create`** | Allows or denies the ability to set a field's value when creating a new document. [More details](#create). |
| **`read`** | Allows or denies the ability to read a field's value. [More details](#read). |
| **`update`** | Allows or denies the ability to update a field's value [More details](#update). |
### Create
Returns a boolean which allows or denies the ability to set a field's value when creating a new document. If `false` is returned, any passed values will be discarded.
@@ -47,7 +69,7 @@ Returns a boolean which allows or denies the ability to set a field's value when
You can define Global-level Access Control within each Global's `access` property. All Access Control functions accept one `args` argument.
Global Access Control is [Access Control](../access-control) used to restrict access to [Global](../globals/overview) Documents, as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Global.
| **`readVersions`** | Used to control who can read versions, and who can't. Will automatically restrict the Admin UI version viewing access. [More details](#read-versions). |
### Read
Returns a boolean result or optionally a [query constraint](/docs/queries/overview) which limits who can read this global based on its current properties.
Returns a boolean result or optionally a [query constraint](../queries/overview) which limits who can read this global based on its current properties.
**Available argument properties:**
To add read Access Control to a [Global](../configuration/globals), use the `read` property in the [Global Config](../globals/overview):
```ts
import { GlobalConfig } from 'payload'
const Header: GlobalConfig = {
// ...
// highlight-start
read: {
read: ({ req: { user } }) => {
return Boolean(user)
},
}
// highlight-end
}
```
The following arguments are provided to the `read` function:
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
### Update
Returns a boolean result or optionally a [query constraint](/docs/queries/overview) which limits who can update this global based on its current properties.
Returns a boolean result or optionally a [query constraint](../queries/overview) which limits who can update this global based on its current properties.
**Available argument properties:**
To add update Access Control to a [Global](../configuration/globals), use the `access` property in the [Global Config](../globals/overview):
```ts
import { GlobalConfig } from 'payload'
const Header: GlobalConfig = {
// ...
// highlight-start
access: {
update: ({ req: { user }, data }) => {
return Boolean(user)
},
}
// highlight-end
}
```
The following arguments are provided to the `update` function:
| **`req`** | The Express `request` object containing the currently authenticated `user` |
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |
| **`data`** | The data passed to update the global with. |
### Read Versions
If the Global has [Versions](../versions/overview) enabled, the `readVersions` Access Control function determines whether or not the currently logged in user can access the version history of a Document.
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Global Config](../globals/overview):
desc: Payload makes it simple to define and manage access control. By declaring roles, you can set permissions and restrict what your users can interact with.
desc: Payload makes it simple to define and manage Access Control. By declaring roles, you can set permissions and restrict what your users can interact with.
Access control within Payload is extremely powerful while remaining easy and intuitive to manage. Declaring who should have access to what documents is no more complex than writing a simple JavaScript function that either returns a `boolean` or a [`query`](/docs/queries/overview) constraint to restrict which documents users can interact with.
<YouTube id="DoPLyXG26Dg" title="Overview of Payload Access Control" />
**Example use cases:**
Access Control determines what a user can and cannot do with any given Document, as well as what they can and cannot see within the [Admin Panel](../admin/overview). By implementing Access Control, you can define granular restrictions based on the user, their roles (RBAC), Document data, or any other criteria your application requires.
- Allowing anyone `read` access to all `Post`s
- Only allowing public access to `Post`s where a `status` field is equal to `published`
- Giving only `User`s with a `role` field equal to `admin` the ability to delete `Page`(s)
- Allowing anyone to create `ContactSubmission`s, but only logged in users to `read`, `update` or `delete` them
- Restricting a `User` to only be able to see their own `Order`(s), but no others
- Allowing `User`s that belong to a certain `Organization` to access only that `Organization`'s `Resource`s
Access Control functions are scoped to the _operation_, meaning you can have different rules for `create`, `read`, `update`, `delete`, etc. Access Control functions are executed _before_ any changes are made and _before_ any operations are completed. This allows you to determine if the user has the necessary permissions before fulfilling the request.
### Default Settings
There are many use cases for Access Control, including:
**By default, all Collections and Globals require that a user is logged in to be able to interact in any way.** The default Access Control function evaluates the `user` from the Express `req` and returns `true` if a user is logged in, and `false` if not.
- Allowing anyone `read` access to all posts
- Only allowing public access to posts where a `status` field is equal to `published`
- Giving only users with a `role` field equal to `admin` the ability to delete posts
- Allowing anyone to submit contact forms, but only logged in users to `read`, `update` or `delete` them
- Restricting a user to only be able to see their own orders, but noone else's
- Allowing users that belong to a certain organization to access only that organization's resources
**Default Access function:**
There are three main types of Access Control in Payload:
- [Collection Access Control](./collections)
- [Global Access Control](./globals)
- [Field Access Control](./fields)
## Default Access Control
Payload provides default Access Control so that your data is secured behind [Authentication](../authentication) without additional configuration. To do this, Payload sets a default function that simply checks if a user is present on the request. You can override this default behavior by defining your own Access Control functions as needed.
Here is the default Access Control that Payload provides:
In the Local API, all Access Control functions are skipped by default, allowing your server to do
whatever it needs. But, you can opt back in by setting the option <strong>
overrideAccess
</strong>{' '}
to <strong>false</strong>.
<Banner type="warning">
<strong>Important:</strong>
In the [Local API](../local-api/overview), all Access Control is _skipped_ by default. This allows your server to have full control over your application. To opt back in, you can set the `overrideAccess` option to `false` in your requests.
</Banner>
### Access Control Types
## The Access Operation
You can manage access within Payload on three different levels:
The Admin Panel responds dynamically to your changes to Access Control. For example, if you restrict editing `ExampleCollection` to only users that feature an "admin" role, Payload will **hide** that Collection from the Admin Panel entirely. This is super powerful and allows you to control who can do what within your Admin Panel using the same functions that secure your APIs.
- [Collections](/docs/access-control/collections)
- [Fields](/docs/access-control/fields)
- [Globals](/docs/access-control/globals)
### When Access Control is Executed
<Banner type="success">
<strong>Note:</strong>
<br />
Access control functions are utilized in two places. It's important to understand how and when
your access control is executed.
</Banner>
#### As you execute operations
When you perform Payload operations like `create`, `read`, `update`, and `delete`, your access control functions will be executed before any changes or operations are completed.
#### Within the Admin UI
The Payload Admin UI responds dynamically to the access control that you define. For example, if you restrict editing a `ExampleCollection` to only users that feature a `role` of `admin`, the Payload Admin UI will **hide** the `ExampleCollection` from the Admin UI entirely. This is super powerful and allows you to control who can do what with your Admin UI.
To accomplish this, Payload ships with an `Access` operation, which is executed when a user logs into the Admin UI. Payload will execute each one of your access control functions, across all collections, globals, and fields, at the top level and return a response that contains a reflection of what the currently authenticated user can do with your application.
### Argument Availability
To accomplish this, Payload exposes the [Access Operation](../authentication/operations#access). Upon login, Payload executes each Access Control function at the top level, across all Collections, Globals, and Fields, and returns a response that contains a reflection of what the currently authenticated user can do within your application.
<Banner type="warning">
<strong>Important:</strong>
<br />
When your access control functions are executed via the <strong>access</strong> operation, the{' '}
<strong>id</strong> and <strong>data</strong> arguments will be <strong>undefined</strong>,
because Payload is executing your functions without referencing a specific document.
When your access control functions are executed via the [Access Operation](../authentication/operations#access), the `id` and `data` arguments will be `undefined`. This is because Payload is executing your functions without referencing a specific Document.
</Banner>
If you use `id` or `data` within your access control functions, make sure to check that they are defined first. If they are not, then you can assume that your access control is being executed via the `access` operation, to determine solely what the user can do within the Admin UI.
If you use `id` or `data` within your access control functions, make sure to check that they are defined first. If they are not, then you can assume that your Access Control is being executed via the Access Operation to determine solely what the user can do within the Admin Panel.
desc: Bundlers are used to bundle the code that serves Payload's Admin Panel.
---
Payload has two official bundlers, the [Webpack Bundler](/docs/admin/webpack) and the [Vite Bundler](/docs/admin/vite). You must install a bundler to use the admin panel.
##### Install a bundler
Webpack (recommended):
```text
yarn add @payloadcms/bundler-webpack
```
Vite (beta):
```text
yarn add @payloadcms/bundler-vite
```
##### Configure the bundler
```ts
// payload.config.ts
import { buildConfig } from 'payload/config'
import { webpackBundler } from '@payloadcms/bundler-webpack'
// import { viteBundler } from '@payloadcms/bundler-vite'
export default buildConfig({
// highlight-start
admin: {
bundler: webpackBundler() // or viteBundler()
},
// highlight-end
})
```
### What are bundlers?
At their core, a bundler's main goal is to take a bunch of files and turn them into a few optimized files that you ship to the browser. The admin UI has a root `index.html` entry point, and from there the bundler traverses the dependency tree, bundling all of the files that are required from that point on.
Since the bundled file is sent to the browser, it can't include any server-only code. You will need to remove any server-only code from your admin UI before bundling it. You can learn more about [excluding server code](/docs/admin/excluding-server-code) section.
<Banner type="warning">
<strong>Using environment variables in the admin UI</strong>
<br />
Bundles should not contain sensitive information. By default, Payload
excludes env variables from the bundle. If you need to use env variables in your payload config,
you need to prefix them with `PAYLOAD_PUBLIC_` to make them available to the client-side code.
The behavior of [Collections](../configuration/collections) within the [Admin Panel](./overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](./components), selecting which fields to display in the List View, and more.
To configure Admin Options for Collections, use the `admin` property in your Collection Config:
| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |
| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Collection from navigation and admin routing. |
| **`hooks`** | Admin-specific hooks for this Collection. [More details](../hooks/collections). |
| **`useAsTitle`** | Specify a top-level field to use for a document title throughout the Admin Panel. If no field is defined, the ID of the document is used as the title. |
| **`description`** | Text to display below the Collection label in the List View to give editors more information. Alternatively, you can use the `admin.components.Description` to render a React component. [More details](#components). |
| **`defaultColumns`** | Array of field names that correspond to which columns to show by default in this Collection's List View. |
| **`hideAPIURL`** | Hides the "API URL" meta field while editing documents within this Collection. |
| **`enableRichTextLink`** | The [Rich Text](../fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| **`enableRichTextRelationship`** | The [Rich Text](../fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| **`meta`** | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](./metadata). |
| **`preview`** | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](#preview). |
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| **`components`** | Swap in your own React components to be used within this Collection. [More details](#components). |
| **`listSearchableFields`** | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |
| **`pagination`** | Set pagination-specific options for this Collection. [More details](#pagination). |
### Components
Collections can set their own [Custom Components](./components) which only apply to [Collection](../configuration/collections)-specific UI within the [Admin Panel](./overview). This includes elements such as the Save Button, or entire layouts such as the Edit View.
To override Collection Components, use the `admin.components` property in your [Collection Config](../configuration/collections):
```ts
import type { SanitizedCollectionConfig } from 'payload'
| **`beforeList`** | An array of components to inject _before_ the built-in List View |
| **`beforeListTable`** | An array of components to inject _before_ the built-in List View's table |
| **`afterList`** | An array of components to inject _after_ the built-in List View |
| **`afterListTable`** | An array of components to inject _after_ the built-in List View's table
| **`Description`** | A component to render below the Collection label in the List View. An alternative to the `admin.description` property. |
| **`edit.SaveButton`** | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. |
| **`edit.SaveDraftButton`** | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. |
| **`edit.PublishButton`** | Replace the default Publish Button with a Custom Component. [Drafts](../versions/drafts) must be enabled. |
| **`edit.PreviewButton`** | Replace the default Preview Button with a Custom Component. [Preview](#preview) must be enabled. |
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
<Banner type="success">
<strong>Note:</strong>
For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components).
</Banner>
### Preview
It is possible to display a Preview Button within the Edit View of the Admin Panel. This will allow editors to visit the frontend of your app the corresponds to the document they are actively editing. This way they can preview the latest, potentially unpublished changes.
To configure the Preview Button, set the `admin.preview` property to a function in your [Collection Config](../configuration/collections):
```ts
import { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
admin: {
// highlight-start
preview: (doc, { locale }) => {
if (doc?.slug) {
return `/${doc.slug}?locale=${locale}`
}
return null
},
// highlight-end
},
}
```
The preview function receives two arguments:
| Argument | Description |
| --- | --- |
| **`doc`** | The Document being edited. |
| **`ctx`** | An object containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT. |
<Banner type="success">
<strong>Note:</strong>
For fully working example of this, check of the official [Draft Preview Example](https://github.com/payloadcms/payload/tree/main/examples/draft-preview) in the [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples).
</Banner>
### Pagination
All Collections receive their own List View which displays a paginated list of documents that can be sorted and filtered. The pagination behavior of the List View can be customized on a per-Collection basis, and uses the same [Pagination](../queries/pagination) API that Payload provides.
To configure pagination options, use the `admin.pagination` property in your [Collection Config](../configuration/collections):
| `defaultLimit` | Integer that specifies the default per-page limit that should be used. Defaults to 10. |
| `limits` | Provide an array of integers to use as per-page options for admins to choose from in the List View. |
### List Searchable Fields
In the List View, there is a "search" box that allows you to quickly find a document through a simple text search. By default, it searches on the ID field. If defined, the `admin.useAsTitle` field is used. Or, you can explicitly define which fields to search based on the needs of your application.
To define which fields should be searched, use the `admin.listSearchableFields` property in your [Collection Config](../configuration/collections):
```ts
import { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
admin: {
// highlight-start
listSearchableFields: ['title', 'slug'],
// highlight-end
},
}
```
<Banner type="warning">
<strong>Tip:</strong>
If you are adding `listSearchableFields`, make sure you index each of these fields so your admin queries can remain performant.
desc: Customize your Payload admin panel further by adding your own CSS or SCSS style sheet to the configuration, powerful theme and design options are waiting for you.
desc: Customize the Payload Admin Panel further by adding your own CSS or SCSS style sheet to the configuration, powerful theme and design options are waiting for you.
Customizing the Payload [Admin Panel](./overview) through CSS alone is one of the easiest and most powerful ways to customize the look and feel of the dashboard. To allow for this level of customization, Payload:
You can add your own CSS by providing your base Payload config with a path to your own CSS or SCSS. Customize the styling of any part of the Payload dashboard as necessary.
1. Exposes a [root-level stylesheet](#global-css) for you to easily to inject custom selectors
1. Provides a [CSS library](#css-library) that can be easily overridden or extended
1. Uses [BEM naming conventions](http://getbem.com) so that class names are globally accessible
To do so, provide your base Payload config with a path to your own stylesheet. It can be either a CSS or SCSS file.
To customize the CSS within the Admin UI, determine scope and change you'd like to make, and then add your own CSS or SCSS to the configuration as needed.
**Example in payload.config.js:**
## Global CSS
```ts
import { buildConfig } from 'payload/config'
import path from 'path'
Global CSS refers to the CSS that is applied to the entire [Admin Panel](./overview). This is where you can have a significant impact to the look and feel of the Admin UI through just a few lines of code.
You can add your own global CSS through the root `custom.scss` file of your app. This file is loaded into the root of the Admin Panel and can be used to inject custom selectors or styles however needed.
Here is an example of how you might target the Dashboard View and change the background color:
```scss
.dashboard {
background-color: red; // highlight-line
}
```
### Overriding built-in styles
To make it as easy as possible for you to override our styles, Payload uses [BEM naming conventions](http://getbem.com/) for all CSS within the Admin UI. If you provide your own CSS, you can override any built-in styles easily.
In addition to adding your own style definitions, you can also override Payload's built-in CSS variables. We use as much as possible behind the scenes, and you can override any of them that you'd like to.
You can find the built-in Payload CSS variables within [`./src/admin/scss/app.scss`](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/scss/app.scss) and [`./src/admin/scss/colors.scss`](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/scss/colors.scss). The following variables are defined and can be overridden:
- Breakpoints
- Base color shades (white to black by default)
- Success / warning / error color shades
- Theme-specific colors (background, input background, text color, etc.)
- Elevation colors (used to determine how "bright" something should be when compared to the background)
- Fonts
- Horizontal gutter
#### Dark mode
<Banner type="warning">
If you're overriding colors or theme elevations, make sure to consider how your changes will
affect dark mode.
<strong>Note:</strong>
If you are building [Custom Components](./overview), it is best to import your own stylesheets directly into your components, rather than using the global stylesheet. You can continue to use the [CSS library](#css-library) as needed.
</Banner>
By default, Payload automatically overrides all `--theme-elevation`s and inverts all success / warning / error shades to suit dark mode. We also update some base theme variables like `--theme-bg`, `--theme-text`, etc.
## Re-using Payload SCSS variables and utilities
You can re-use Payload's SCSS variables and utilities in your own stylesheets by importing it from the UI package.
```scss
@import '~@payloadcms/ui/scss';
```
## CSS Library
To make it as easy as possible for you to override default styles, Payload uses [BEM naming conventions](http://getbem.com/) for all CSS within the Admin UI. If you provide your own CSS, you can override any built-in styles easily, including targeting nested components and their various component states.
You can also override Payload's built-in [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties). These variables are widely consumed by the Admin Panel, so modifying them has a significant impact on the look and feel of the Admin UI.
The following variables are defined and can be overridden:
For an up-to-date, comprehensive list of all available variables, please refer to the [Source Code](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss).
<Banner type="warning">
<strong>Warning:</strong>
If you're overriding colors or theme elevations, make sure to consider how [your changes will affect dark mode](#dark-mode).
</Banner>
#### Dark Mode
Colors are designed to automatically adapt to theme of the [Admin Panel](./overview). By default, Payload automatically overrides all `--theme-elevation` colors and inverts all success / warning / error shades to suit dark mode. We also update some base theme variables like `--theme-bg`, `--theme-text`, etc.
This key will automatically be made available to the Payload bundle and can be referenced in your Admin component code as `process.env.PAYLOAD_PUBLIC_STRIPE_PUBLISHABLE_KEY`.
desc: Learn how to exclude server-only code from the Payload Admin UI bundle
---
Because the Admin Panel browser bundle includes your Payload Config file, files using server-only modules need to be excluded.
It's common for your config to rely on server only modules to perform logic in access control functions, hooks, and other contexts.
Any file that imports a server-only module such as `fs`, `stripe`, `authorizenet`, `nodemailer`, etc. **cannot** be included in the browser bundle.
#### Example Scenario
Say we have a collection called `Subscriptions` that has a `beforeChange` hook that creates a Stripe subscription whenever a Subscription document is created in Payload.
**Collection config**:
```ts
// collections/Subscriptions/index.ts
import { CollectionConfig } from 'payload/types'
import createStripeSubscription from './hooks/createStripeSubscription'
The above code is NOT production-ready and should not be referenced to create Stripe
subscriptions. Although creating a beforeChange hook is a completely valid spot to do things like
create subscriptions, the code above is incomplete and insecure, meant for explanation purposes
only.
</Banner>
**As-is, this collection will prevent your Admin panel from bundling or loading correctly, because Stripe relies on some Node-only packages.**
#### How to fix this
You need to make sure that you use `alias`es to tell your bundler to import "safe" files vs. attempting to import any server-side code that you need to get rid of. Depending on your bundler (Webpack, Vite, etc.) the steps involved may be slightly different.
The basic idea is to create a file that exports an empty object, and then alias import paths of any files that import server-only modules to that empty object file.
This way when your bundler goes to import a file that contains server-only modules, it will instead import the empty object file, which will not break the browser bundle.
### Aliasing server-only modules
To remove files that contain server-only modules from your bundle, you can use an `alias`.
In the Subscriptions config file above, we are importing the hook like so:
```ts
// collections/Subscriptions/index.ts
import createStripeSubscription from './hooks/createStripeSubscription'
```
By default the browser bundle will now include all the code from that file and any files down the tree. We know that the file imports `stripe`.
To fix this, we need to alias the `createStripeSubscription` file to a different file that can safely be included in the browser bundle.
First, we will create a mock file to replace the server-only file when bundling:
```js
// mocks/modules.js
export default {}
/**
* NOTE: if you are destructuring an import
* the mock file will need to export matching
* variables as the destructured object.
*
* export const namedExport = {}
*/
```
Aliasing with [Webpack](/docs/admin/webpack) can be done by:
```ts
// payload.config.ts
import { buildConfig } from 'payload/config'
import { webpackBundler } from '@payloadcms/bundler-webpack'
import { Subscriptions } from './collections/Subscriptions'
[Fields](../fields/overview) within the [Admin Panel](./overview) can be endlessly customized in their appearance and behavior without affecting their underlying data structure. Fields are designed to withstand heavy modification or even complete replacement through the use of [Custom Field Components](#field-components), [Conditional Logic](#conditional-logic), [Custom Validations](../fields/overview#validation), and more.
For example, your app might need to render a specific interface that Payload does not inherently support, such as a color picker. To do this, you could replace the default [Text Field](../fields/text) input with your own user-friendly component that formats the data into a valid color value.
<Banner type="success">
<strong>Tip:</strong>
Don't see a built-in field type that you need? Build it! Using a combination of [Field Validations](../fields/overview#validation)
and [Custom Components](./components), you can override the entirety of how a component functions within the [Admin Panel](./overview) to effectively create your own field type.
</Banner>
## Admin Options
You can customize the appearance and behavior of fields within the [Admin Panel](./overview) through the `admin` property of any [Field Config](../fields/overview):
| **`condition`** | Programmatically show / hide fields based on other fields. [More details](../admin/fields#conditional-logic). |
| **`components`** | All Field Components can be swapped out for [Custom Components](../admin/components) that you define. [More details](../admin/fields). |
| **`description`** | Helper text to display alongside the field to provide more information for the editor. [More details](../admin/fields#description). |
| **`position`** | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
| **`width`** | Restrict the width of a field. You can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
| **`style`** | [CSS Properties](https://developer.mozilla.org/en-US/docs/Web/CSS) to inject into the root element of the field. |
| **`className`** | Attach a [CSS class attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Class_selectors) to the root DOM element of a field. |
| **`readOnly`** | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview). |
| **`disableBulkEdit`** | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
| **`disableListColumn`** | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |
| **`disableListFilter`** | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |
| **`hidden`** | Will transform the field into a `hidden` input type. Its value will still submit with requests in the Admin Panel, but the field itself will not be visible to editors. |
## Field Components
Within the [Admin Panel](./overview), fields are rendered in three distinct places:
- [Field](#the-field-component) - The actual form field rendered in the Edit View.
- [Cell](#the-cell-component) - The table cell component rendered in the List View.
- [Filter](#the-filter-component) - The filter component rendered in the List View.
To easily swap in Field Components with your own, use the `admin.components` property in your [Field Config](../fields/overview):
| **`Field`** | The form field rendered of the Edit View. [More details](#the-field-component). |
| **`Cell`** | The table cell rendered of the List View. [More details](#the-cell-component). |
| **`Filter`** | The filter component rendered in the List View. [More details](#the-filter-component). || Component | Description |
| **`Label`** | Override the default Label of the Field Component. [More details](#the-label-component). |
| **`Error`** | Override the default Error of the Field Component. [More details](#the-error-component). |
| **`Description`** | Override the default Description of the Field Component. [More details](#the-description-component). |
| **`beforeInput`** | An array of elements that will be added before the input of the Field Component. [More details](#afterinput-and-beforeinput).|
| **`afterInput`** | An array of elements that will be added after the input of the Field Component. [More details](#afterinput-and-beforeinput). |
_\* **`beforeInput`** and **`afterInput`** are only supported in fields that do not contain other fields, such as [`Text`](../fields/text), and [`Textarea`](../fields/textarea)._
### The Field Component
The Field Component is the actual form field rendered in the Edit View. This is the input that user's will interact with when editing a document.
To easily swap in your own Field Component, use the `admin.components.Field` property in your [Field Config](../fields/overview):
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
<Banner type="warning">
Instead of replacing the entire Field Component, you can alternately replace or slot-in only specific parts by using the [`Label`](#the-label-component), [`Error`](#the-error-component), [`beforeInput`](#afterinput-and-beforinput), and [`afterInput`](#afterinput-and-beforinput) properties.
| **`docPreferences`** | An object that contains the [Preferences](./preferences) for the document.
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
| **`locale`** | The locale of the field. [More details](../configuration/localization). |
| **`readOnly`** | A boolean value that represents if the field is read-only or not. |
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
| **`validate`** | A function that can be used to validate the field. |
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### Sending and receiving values from the form
When swapping out the `Field` component, you are responsible for sending and receiving the field's `value` from the form itself.
To do so, import the [`useField`](./hooks#usefield) hook from `@payloadcms/ui` and use it to manage the field's value:
For a complete list of all available React hooks, see the [Payload React Hooks](./hooks) documentation. For additional help, see [Building Custom Components](./components#building-custom-components).
</Banner>
#### TypeScript
When building Custom Field Components, you can import the component type to ensure type safety. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and for every client/server environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
```tsx
import type {
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
// ...and so on for each Field Type
} from 'payload'
```
### The `field` Prop
All Field Components are passed their own Field Config through a common `field` prop. Within Server Components, this is the original Field Config as written within your Payload Config. Within Client Components, however, this is a "Client Config", which is a sanitized, client-friendly version of the Field Config. This is because the original Field Config is [non-serializable](https://react.dev/reference/rsc/use-client#serializable-types), meaning it cannot be passed into Client Components without first being transformed.
The Client Field Config is an exact copy of the original Field Config, minus all non-serializable properties, plus all evaluated functions such as field labels, [Custom Components](../components), etc.
Server Component:
```tsx
import React from 'react'
import type { TextFieldServerComponent } from 'payload'
| **`_isPresentational`** | A boolean indicating that the field is purely visual and does not directly affect data or change data shape, i.e. the [UI Field](../fields/ui). |
| **`_path`** | A string representing the direct, dynamic path to the field at runtime, i.e. `myGroup.myArray[0].myField`. |
| **`_schemaPath`** | A string representing the direct, static path to the [Field Config](../fields/overview), i.e. `myGroup.myArray.myField` |
<Banner type="info">
<strong>Note:</strong>
These properties are underscored to denote that they are not part of the original Field Config, and instead are attached during client sanitization to make fields easier to work with on the front-end.
</Banner>
#### TypeScript
When building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
```tsx
import type {
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
// ...and so on for each Field Type
} from 'payload'
```
### The Cell Component
The Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.
To easily swap in your own Cell Component, use the `admin.components.Cell` property in your [Field Config](../fields/overview):
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
| **`link`** | A boolean representing whether this cell should be wrapped in a link. |
| **`onClick`** | A function that is called when the cell is clicked. |
<Banner type="info">
<strong>Tip:</strong>
Use the [`useTableCell`](./hooks#usetablecell) hook to subscribe to the field's `cellData` and `rowData`.
</Banner>
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
### The Label Component
The Label Component is rendered anywhere a field needs to be represented by a label. This is typically used in the Edit View, but can also be used in the List View and elsewhere.
To easily swap in your own Label Component, use the `admin.components.Label` property in your [Field Config](../fields/overview):
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### TypeScript
When building Custom Label Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Label Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `LabelServerComponent` or `LabelClientComponent` to the type of field, i.e. `TextFieldLabelClientComponent`.
```tsx
import type {
TextFieldLabelServerComponent,
TextFieldLabelClientComponent,
// ...and so on for each Field Type
} from 'payload'
```
### The Error Component
The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.
To easily swap in your own Error Component, use the `admin.components.Error` property in your [Field Config](../fields/overview):
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### TypeScript
When building Custom Error Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Error Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `ErrorServerComponent` or `ErrorClientComponent` to the type of field, i.e. `TextFieldErrorClientComponent`.
```tsx
import type {
TextFieldErrorServerComponent,
TextFieldErrorClientComponent,
// And so on for each Field Type
} from 'payload'
```
### The Description Property
Field Descriptions are used to provide additional information to the editor about a field, such as special instructions. Their placement varies from field to field, but typically are displayed with subtle style differences beneath the field inputs.
A description can be configured in three ways:
- As a string.
- As a function which returns a string. [More details](#description-functions).
- As a React component. [More details](#the-description-component).
To easily add a Custom Description to a field, use the `admin.description` property in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
To replace the Field Description with a [Custom Component](./components), use the `admin.components.Description` property. [More details](#the-description-component).
</Banner>
#### Description Functions
Custom Descriptions can also be defined as a function. Description Functions are executed on the server and can be used to format simple descriptions based on the user's current [Locale](../configuration/localization).
To easily add a Description Function to a field, set the `admin.description` property to a _function_ in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
| **`t`** | The `t` function used to internationalize the Admin Panel. [More details](../configuration/i18n) |
### The Description Component
Alternatively to the [Description Property](#the-description-property), you can also use a [Custom Component](./components) as the Field Description. This can be useful when you need to provide more complex feedback to the user, such as rendering dynamic field values or other interactive elements.
To easily add a Description Component to a field, use the `admin.components.Description` property in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
| **`field`** | In Server Components, this is the original Field Config. In Client Components, this is the sanitized Client Field Config. [More details](#the-field-prop). |
| **`clientField`** | Server components receive the Client Field Config through this prop. [More details](#the-field-prop). |
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### TypeScript
When building Custom Description Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Description Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `DescriptionServerComponent` or `DescriptionClientComponent` to the type of field, i.e. `TextFieldDescriptionClientComponent`.
```tsx
import type {
TextFieldDescriptionServerComponent,
TextFieldDescriptionClientComponent,
// And so on for each Field Type
} from 'payload'
```
### afterInput and beforeInput
With these properties you can add multiple components _before_ and _after_ the input element, as their name suggests. This is useful when you need to render additional elements alongside the field without replacing the entire field component.
To add components before and after the input element, use the `admin.components.beforeInput` and `admin.components.afterInput` properties in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
## Conditional Logic
You can show and hide fields based on what other fields are doing by utilizing conditional logic on a field by field basis. The `condition` property on a field's admin config accepts a function which takes three arguments:
- `data` - the entire document's data that is currently being edited
- `siblingData` - only the fields that are direct siblings to the field with the condition
- `{ user }` - the final argument is an object containing the currently authenticated user
The `condition` function should return a boolean that will control if the field should be displayed or not.
The behavior of [Globals](../configuration/globals) within the [Admin Panel](./overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](./components), setting page metadata, and more.
To configure Admin Options for Globals, use the `admin` property in your Global Config:
| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |
| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Global from navigation and admin routing. |
| **`components`** | Swap in your own React components to be used within this Global. [More details](#components). |
| **`preview`** | Function to generate a preview URL within the Admin Panel for this Global that can point to your app. [More details](#preview). |
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| **`hideAPIURL`** | Hides the "API URL" meta field while editing documents within this collection. |
| **`meta`** | Page metadata overrides to apply to this Global within the Admin Panel. [More details](./metadata). |
### Components
Globals can set their own [Custom Components](./components) which only apply to [Global](../configuration/globals)-specific UI within the [Admin Panel](./overview). This includes elements such as the Save Button, or entire layouts such as the Edit View.
To override Global Components, use the `admin.components` property in your [Global Config](../configuration/globals):
```ts
import type { SanitizedGlobalConfig } from 'payload'
| **`elements.SaveButton`** | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. |
| **`elements.SaveDraftButton`** | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. |
| **`elements.PublishButton`** | Replace the default Publish Button with a Custom Component. [Drafts](../versions/drafts) must be enabled. |
| **`elements.PreviewButton`** | Replace the default Preview Button with a Custom Component. [Preview](#preview) must be enabled. |
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
<Banner type="success">
<strong>Note:</strong>
For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components).
</Banner>
### Preview
It is possible to display a Preview Button within the Edit View of the Admin Panel. This will allow editors to visit the frontend of your app the corresponds to the document they are actively editing. This way they can preview the latest, potentially unpublished changes.
To configure the Preview Button, set the `admin.preview` property to a function in your Global Config:
```ts
import { GlobalConfig } from 'payload'
export const MainMenu: GlobalConfig = {
// ...
admin: {
// highlight-start
preview: (doc, { locale }) => {
if (doc?.slug) {
return `/${doc.slug}?locale=${locale}`
}
return null
},
// highlight-end
},
}
```
The preview function receives two arguments:
| Argument | Description |
| --- | --- |
| **`doc`** | The Document being edited. |
| **`ctx`** | An object containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT. |
<Banner type="success">
<strong>Note:</strong>
For fully working example of this, check of the official [Draft Preview Example](https://github.com/payloadcms/payload/tree/main/examples/draft-preview) in the [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples).
Payload provides a variety of powerful hooks that can be used within your own React components. With them, you can interface with Payload itself and build just about any type of complex customization you can think of—directly in familiar React code.
Payload provides a variety of powerful [React Hooks](https://react.dev/reference/react-dom/hooks) that can be used within your own [Custom Components](./components), such as [Custom Fields](./fields). With them, you can interface with Payload itself to build just about any type of complex customization you can think of.
### useField
<Banner type="warning">
<strong>Reminder:</strong>
All Custom Components are [React Server Components](https://react.dev/reference/rsc/server-components) by default. Hooks, on the other hand, are only available in client-side environments. To use hooks, [ensure your component is a client component](./components#client-components).
</Banner>
The `useField` hook is used internally within every applicable Payload field component, and it manages sending and receiving a field's state from its parent form.
## useField
Outside of internal use, its most common use-case is in custom `Field` components. When you build a custom React `Field` component, you'll be responsible for sending and receiving the field's `value` from the form itself. To do so, import the `useField` hook as follows:
The `useField` hook is used internally within all field components. It manages sending and receiving a field's state from its parent form. When you build a [Custom Field Component](./fields), you will be responsible for sending and receiving the field's `value` to and from the form yourself.
To do so, import the `useField` hook as follows:
```tsx
import { useField } from 'payload/components/forms'
| `path` | If you do not provide a `path` or a `name`, this hook will look for one using the [`useFieldProps`](#usefieldprops) hook. |
| `validate` | A validation function executed client-side _before_ submitting the form to the server. Different than [Field-level Validation](../fields/overview#validation) which runs strictly on the server. |
| `disableFormData` | If `true`, the field will not be included in the form data when the form is submitted. |
| `hasRows` | If `true`, the field will be treated as a field with rows. This is useful for fields like `array` and `blocks`. |
The `useField` hook returns the following object:
```ts
const field = useField<string>({
path: 'fieldPathHere', // required
validate: myValidateFunc, // optional
disableFormData?: false, // if true, the field's data will be ignored
condition?: myConditionHere, // optional, used to skip validation if condition fails
})
// Here is what `useField` sends back
const {
showError, // whether or not the field should show as errored
errorMessage, // the error message to show, if showError
value, // the current value of the field from the form
formSubmitted, // if the form has been submitted
formProcessing, // if the form is currently processing
setValue, // method to set the field's value in form state
initialValue, // the initial value that the field mounted with
[Custom Field Components](./fields#the-field-component) can be rendered on the server. When using a server component as a custom field component, you can access dynamic props from within any client component rendered by your custom server component. This is done using the `useFieldProps` hook. This is important because some fields can be dynamic, such as when nested in an [`array`](../fields/array) or [`blocks`](../fields/block) field. For example, items can be added, re-ordered, or deleted on-the-fly.
You can use the `useFieldProps` hooks to access dynamic props like `path`:
The [`useField`](#usefield) hook calls the `useFieldProps` hook internally, so you don't need to use both in the same component unless explicitly needed.
</Banner>
## useFormFields
There are times when a custom field component needs to have access to data from other fields, and you have a few options to do so. The `useFormFields` hook is a powerful and highly performant way to retrieve a form's field state, as well as to retrieve the `dispatchFields` method, which can be helpful for setting other fields' form states from anywhere within a form.
@@ -66,7 +112,8 @@ Thanks to the awesome package [`use-context-selector`](https://github.com/dai-sh
You can pass a Redux-like selector into the hook, which will ensure that you retrieve only the field that you want. The selector takes an argument with type of `[fields: Fields, dispatch: React.Dispatch<Action>]]`.
```tsx
import { useFormFields } from 'payload/components/forms'
'use client'
import { useFormFields } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// Get only the `amount` field state, and only cause a rerender when that field changes
**To retrieve more than one field**, you can use the `useAllFormFields` hook. Your component will re-render when _any_ field changes, so use this hook only if you absolutely need to. Unlike the `useFormFields` hook, this hook does not accept a "selector", and it always returns an array with type of `[fields: Fields, dispatch: React.Dispatch<Action>]]`.
You can do lots of powerful stuff by retrieving the full form state, like using built-in helper functions to reduce field state to values only, or to retrieve sibling data by path.
```tsx
import { useAllFormFields, reduceFieldsToValues, getSiblingData } from 'payload/components/forms';
'use client'
import { useAllFormFields } from '@payloadcms/ui'
import { reduceFieldsToValues, getSiblingData } from 'payload/shared'
const ExampleComponent: React.FC = () => {
// the `fields` const will be equal to all fields' state,
If you are building a custom component, then you should use `setValue` which is returned from the `useField` hook to programmatically set your field's value. But if you're looking to update _another_ field's value, you can use `dispatchFields` returned from `useFormFields`.
If you are building a Custom Component, then you should use `setValue` which is returned from the `useField` hook to programmatically set your field's value. But if you're looking to update _another_ field's value, you can use `dispatchFields` returned from `useFormFields`.
You can send the following actions to the `dispatchFields` function.
@@ -128,7 +177,7 @@ You can send the following actions to the `dispatchFields` function.
To see types for each action supported within the `dispatchFields` hook, check out the Form types [here](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/components/forms/Form/types.ts).
### useForm
## useForm
The `useForm` hook can be used to interact with the form itself, and sends back many methods that can be used to reactively fetch form state without causing rerenders within your components each time a field is changed. This is useful if you have action-based callbacks that your components fire, and need to interact with form state _based on a user action_.
@@ -141,7 +190,7 @@ The `useForm` hook can be used to interact with the form itself, and sends back
up-to-date. They will be removed from this hook's response in an upcoming version.
</Banner>
The `useForm` hook returns an object with the following properties: |
The `useForm` hook returns an object with the following properties:
In any custom component you can get the selected locale object with the `useLocale` hook. `useLocale`gives you the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
In any Custom Component you can get the selected locale object with the `useLocale` hook. `useLocale`gives you the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
```tsx
import { useLocale } from 'payload/components/utilities'
Useful to retrieve info about the currently logged in user as well as methods for interacting with it. It sends back an object with the following properties:
@@ -711,8 +795,9 @@ Useful to retrieve info about the currently logged in user as well as methods fo
| **`permissions`** | The permissions of the current user |
```tsx
import { useAuth } from 'payload/components/utilities'
Used to easily retrieve the Payload [Client Config](./components#accessing-the-payload-config).
```tsx
import { useConfig } from 'payload/components/utilities'
'use client'
import { useConfig } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const config = useConfig()
const { config } = useConfig()
// highlight-end
return <span>{config.serverURL}</span>
}
```
### useEditDepth
## useEditDepth
Sends back how many editing levels "deep" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.
```tsx
import { useEditDepth } from 'payload/components/utilities'
Returns methods to set and get user preferences. More info can be found [here](https://payloadcms.com/docs/admin/preferences).
### useTableColumns
## useTheme
Returns the currently selected theme (`light`, `dark` or `auto`), a set function to update it and a boolean `autoMode`, used to determine if the theme value should be set automatically based on the user's device preferences.
```tsx
'use client'
import { useTheme } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const { autoMode, setTheme, theme } = useTheme()
// highlight-end
return (
<>
<span>
The current theme is {theme} and autoMode is {autoMode}
The `useDocumentEvents` hook provides a way of subscribing to cross-document events, such as updates made to nested documents within a drawer. This hook will report document events that are outside the scope of the document currently being edited. This hook provides the following:
Similar to [`useFieldProps`](#usefieldprops), all [Custom Cell Components](./fields#the-cell-component) are rendered on the server, and as such, only have access to static props at render time. But, some props need to be dynamic, such as the field value itself.
| **`mostRecentUpdate`** | An object containing the most recently updated document. It contains the `entitySlug`, `id` (if collection), and `updatedAt` properties |
| **`reportUpdate`** | A method used to report updates to documents. It accepts the same arguments as the `mostRecentUpdate` property. |
**Example:**
For this reason, dynamic props like `cellData` are managed in their own React context, which can be accessed using the `useTableCell` hook.
```tsx
import { useDocumentEvents } from 'payload/components/hooks'
The `useDocumentEvents` hook provides a way of subscribing to cross-document events, such as updates made to nested documents within a drawer. This hook will report document events that are outside the scope of the document currently being edited. This hook provides the following:
| **`mostRecentUpdate`** | An object containing the most recently updated document. It contains the `entitySlug`, `id` (if collection), and `updatedAt` properties |
| **`reportUpdate`** | A method used to report updates to documents. It accepts the same arguments as the `mostRecentUpdate` property. |
**Example:**
```tsx
'use client'
import { useDocumentEvents } from '@payloadcms/ui'
Right now the `useDocumentEvents` hook only tracks recently updated documents, but in the future it will track more document-related events as needed, such as document creation, deletion, etc.
Right now the `useDocumentEvents` hook only tracks recently updated documents, but in the future
it will track more document-related events as needed, such as document creation, deletion, etc.
Every page within the Admin Panel automatically receives dynamic, auto-generated metadata derived from live document data, the user's current locale, and more, without any additional configuration. This includes the page title, description, og:image and everything in between. Metadata is fully configurable at the root level and cascades down to individual collections, documents, and custom views, allowing for the ability to control metadata on any page with high precision.
Within the Admin Panel, metadata can be customized at the following levels:
- [Root Metadata](#root-metadata)
- [Collection Metadata](#collection-metadata)
- [Global Metadata](#global-metadata)
- [View Metadata](#view-metadata)
All of these types of metadata share a similar structure, with a few key differences on the Root level. To customize metadata, consult the list of available scopes. Determine the scope that corresponds to what you are trying to accomplish, then author your metadata within the Payload Config accordingly.
## Root Metadata
Root Metadata is the metadata that is applied to all pages within the Admin Panel. This is where you can control things like the suffix appended onto each page's title, the favicon displayed in the browser's tab, and the Open Graph data that is used when sharing the Admin Panel on social media.
To customize Root Metadata, use the `admin.meta` key in your Payload Config:
```ts
{
// ...
admin: {
// highlight-start
meta: {
// highlight-end
title: 'My Admin Panel',
description: 'The best admin panel in the world',
icons: [
{
rel: 'icon',
type: 'image/png',
href: '/favicon.png',
},
],
},
},
}
```
The following options are available for Root Metadata:
| Key | Type | Description |
| --- | --- | --- |
| **`title`** | `string` | The title of the Admin Panel. |
| **`description`** | `string` | The description of the Admin Panel. |
| **`defaultOGImageType`** | `dynamic` (default), `static`, or `off` | The type of default OG image to use. If set to `dynamic`, Payload will use Next.js image generation to create an image with the title of the page. If set to `static`, Payload will use the `defaultOGImage` URL. If set to `off`, Payload will not generate an OG image. |
| **`icons`** | `IconConfig[]` | An array of icon objects. [More details](#icons) |
| **`keywords`** | `string` | A comma-separated list of keywords to include in the metadata of the Admin Panel. |
| **`openGraph`** | `OpenGraphConfig` | An object containing Open Graph metadata. [More details](#open-graph) |
| **`titleSuffix`** | `string` | A suffix to append to the end of the title of every page. Defaults to "- Payload". |
<Banner type="success">
<strong>Reminder:</strong>
These are the _root-level_ options for the Admin Panel. You can also customize [Collection Metadata](./collections), [Global Metadata](./globals), and [Document Metadata](./documents) in their respective configs.
</Banner>
### Icons
The Icons Config corresponds to the `<link>` tags that are used to specify icons for the Admin Panel. The `icons` key is an array of objects, each of which represents an individual icon. Icons are differentiated from one another by their `rel` attribute, which specifies the relationship between the document and the icon.
The most common icon type is the favicon, which is displayed in the browser tab. This is specified by the `rel` attribute `icon`. Other common icon types include `apple-touch-icon`, which is used by Apple devices when the Admin Panel is saved to the home screen, and `mask-icon`, which is used by Safari to mask the Admin Panel icon.
To customize icons, use the `icons` key within the `admin.meta` object in your Payload Config:
```ts
{
// ...
admin: {
meta: {
// highlight-start
icons: [
// highlight-end
{
rel: 'icon',
type: 'image/png',
href: '/favicon.png',
},
{
rel: 'apple-touch-icon',
type: 'image/png',
href: '/apple-touch-icon.png',
},
],
},
},
}
```
The following options are available for Icons:
| Key | Type | Description |
| --- | --- | --- |
| **`rel`** | `string` | The HTML `rel` attribute of the icon. |
| **`type`** | `string` | The MIME type of the icon. |
| **`color`** | `string` | The color of the icon. |
| **`fetchPriority`** | `string` | The [fetch priority](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/fetchPriority) of the icon. |
| **`media`** | `string` | The [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries) of the icon. |
| **`sizes`** | `string` | The [sizes](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/sizes) of the icon. |
| **`url`** | `string` | The URL pointing the resource of the icon. |
### Open Graph
Open Graph metadata is a set of tags that are used to control how URLs are displayed when shared on social media platforms. Open Graph metadata is automatically generated by Payload, but can be customized at the Root level.
To customize Open Graph metadata, use the `openGraph` key within the `admin.meta` object in your Payload Config:
```ts
{
// ...
admin: {
meta: {
// highlight-start
openGraph: {
// highlight-end
description: 'The best admin panel in the world',
images: [
{
url: 'https://example.com/image.jpg',
width: 800,
height: 600,
},
],
siteName: 'Payload',
title: 'My Admin Panel',
},
},
},
}
```
The following options are available for Open Graph Metadata:
| Key | Type | Description |
| --- | --- | --- |
| **`description`** | `string` | The description of the Admin Panel. |
| **`images`** | `OGImageConfig | OGImageConfig[]` | An array of image objects. |
| **`siteName`** | `string` | The name of the site. |
| **`title`** | `string` | The title of the Admin Panel. |
## Collection Metadata
Collection Metadata is the metadata that is applied to all pages within any given Collection within the Admin Panel. This metadata is used to customize the title and description of all views within any given Collection, unless overridden by the view itself.
To customize Collection Metadata, use the `admin.meta` key within your Collection Config:
```ts
import { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: {
// highlight-start
meta: {
// highlight-end
title: 'My Collection',
description: 'The best collection in the world',
},
},
}
```
The Collection Meta config has the same options as the [Root Metadata](#root-metadata) config.
## Global Metadata
Global Metadata is the metadata that is applied to all pages within any given Global within the Admin Panel. This metadata is used to customize the title and description of all views within any given Global, unless overridden by the view itself.
To customize Global Metadata, use the `admin.meta` key within your Global Config:
```ts
import { GlobalConfig } from 'payload'
export const MyGlobal: GlobalConfig = {
// ...
admin: {
// highlight-start
meta: {
// highlight-end
title: 'My Global',
description: 'The best
},
},
}
```
The Global Meta config has the same options as the [Root Metadata](#root-metadata) config.
## View Metadata
View Metadata is the metadata that is applied to specific [Views](./views) within the Admin Panel. This metadata is used to customize the title and description of a specific view, overriding any metadata set at the [Root](#root-metadata), [Collection](#collection-metadata), or [Global](#global-metadata) level.
To customize View Metadata, use the `meta` key within your View Config:
desc: Manage your data and customize the Admin Panel by swapping in your own React components. Create, modify or remove views, fields, styles and much more.
desc: Manage your data and customize the Payload Admin Panel by swapping in your own React components. Create, modify or remove views, fields, styles and much more.
Payload dynamically generates a beautiful, fully functional React admin panel to manage your data. It's extremely powerful and can be customized / extended upon easily by swapping in your own React components. You can add additional views, modify how built-in views look / work, swap out Payload branding for your client's, build your own field types and much more.
Payload dynamically generates a beautiful, [fully type-safe](../typescript/overview) Admin Panel to manage your users and data. It is highly performant, even with 100+ fields, and is translated in over 30 languages. Within the Admin Panel you can manage content, [render your site](../live-preview/overview), preview drafts, [diff versions](../versions/overview), and so much more.
The Payload Admin panel can be bundled with our officially supported [Vite](/docs/admin/vite) and [webpack](/docs/admin/webpack) bundlers or you can integrate another bundler following our adapter pattern approach.
When bundled, it is code-split, highly performant (even with 100+ fields), and written fully in TypeScript.
The Admin Panel is designed to [white-label your brand](https://payloadcms.com/blog/white-label-admin-ui). You can endlessly customize and extend the Admin UI by swapping in your own [Custom Components](./components)—everything from simple field labels to entire views can be modified or replaced to perfectly tailor the interface for your editors.
The Admin Panel is written in [TypeScript](https://www.typescriptlang.org) and built with [React](https://react.dev) using the [Next.js App Router](https://nextjs.org/docs/app). It supports [React Server Components](https://react.dev/reference/rsc/server-components), enabling the use of the [Local API](/docs/local-api/overview) on the front-end. You can install Payload into any [existing Next.js app in just one line](../getting-started/installation) and [deploy it anywhere](../production).
<Banner type="success">
The Admin panel is meant to be simple enough to give you a starting point but not bring too much
complexity, so that you can easily customize it to suit the needs of your application and your
editors.
The Payload Admin Panel is designed to be as minimal and straightforward as possible to allow easy customization and control. [Learn more](./components).
caption="Redesigned admin panel with a collapsible sidebar that's open by default, providing greater extensibility and enhanced horizontal real estate."
alt="Admin Panel with collapsible sidebar"
caption="Redesigned Admin Panel with a collapsible sidebar that's open by default, providing greater extensibility and enhanced horizontal real estate."
/>
## Project Structure
The Admin Panel serves as the entire HTTP layer for Payload, providing a full CRUD interface for your app. This means that both the [REST](../rest-api/overview) and [GraphQL](../graphql/overview) APIs are simply [Next.js Routes](https://nextjs.org/docs/app/building-your-application/routing) that exist directly alongside your front-end application.
Once you [install Payload](../getting-started/installation), the following files and directories will be created in your app:
```plaintext
app/
├─ (payload)/
├── admin/
├─── [[...segments]]/
├──── page.tsx
├──── not-found.tsx
├── api/
├─── [...slug]/
├──── route.ts
├── graphql/
├──── route.ts
├── graphql-playground/
├──── route.ts
├── custom.scss
├── layout.tsx
```
<Banner type="info">
If you are not familiar with Next.js project structure, you can [learn more about it here](https://nextjs.org/docs/getting-started/project-structure).
</Banner>
As shown above, all Payload routes are nested within the `(payload)` route group. This creates a boundary between the Admin Panel and the rest of your application by scoping all layouts and styles. The `layout.tsx` file within this directory, for example, is where Payload manages the `html` tag of the document to set proper [`lang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) and [`dir`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) attributes, etc.
The `admin` directory contains all the _pages_ related to the interface itself, whereas the `api` and `graphql` directories contains all the _routes_ related to the [REST API](../rest-api/overview) and [GraphQL API](../graphql/overview). All admin routes are [easily configurable](#customizing-routes) to meet your application's exact requirements.
<Banner type="warning">
<strong>Note:</strong>
If you don't use the [REST API](../rest/overview) or [GraphQL API](../graphql/overview), you can delete the [Next.js files corresponding to those routes](../admin/overview#project-structure), however, the overhead of this API is completely constrained to these endpoints, and will not slow down or affect Payload outside of the endpoints.
</Banner>
Finally, the `custom.scss` file is where you can add or override globally-oriented styles in the Admin Panel, such as modify the color palette. Customizing the look and feel through CSS alone is a powerful feature of the Admin Panel, [more on that here](./customizing-css).
All auto-generated files will contain the following comments at the top of each file:
```tsx
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */,
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
```
## Admin Options
All options for the Admin panel are defined in your base Payload config file.
All options for the Admin Panel are defined in your [Payload Config](../configuration/overview) under the `admin` property:
| `bundler` | The bundler that you would like to use to bundle the admin panel. Officially supported bundlers: [Webpack](/docs/admin/webpack) and [Vite](/docs/admin/vite). |
| `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) |
| `buildPath` | Specify an absolute path for where to store the built Admin panel bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. |
| `disable` | If set to `true`, the entire Admin panel will be disabled. |
| `indexHTML` | Optionally replace the entirety of the `index.html` file used by the Admin panel. Reference the [base index.html file](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/index.html) to ensure your replacement has the appropriate HTML elements. |
| `css` | Absolute path to a stylesheet that you can use to override / customize the Admin panel styling. [More](/docs/admin/customizing-css). |
| `scss` | Absolute path to a Sass variables / mixins stylesheet meant to override Payload styles to make for an easy re-skinning of the Admin panel. [More](/docs/admin/customizing-css#overriding-scss-variables). |
| `dateFormat` | Global date format that will be used for all dates in the Admin panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
| `autoLogin` | Used to automate admin log-in for dev and demonstration convenience. [More](/docs/authentication/config). |
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
| `components` | Component overrides that affect the entirety of the Admin panel. [More](/docs/admin/components) |
| `webpack` | Customize the Webpack config that's used to generate the Admin panel. [More](/docs/admin/webpack) |
| `vite` | Customize the Vite config that's used to generate the Admin panel. [More](/docs/admin/vite) |
| `logoutRoute` | The route for the `logout` page. |
| `inactivityRoute` | The route for the `logout` inactivity page. |
| **`avatar`** | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
| **`autoLogin`** | Used to automate log-in for dev and demonstration convenience. [More details](../authentication/overview). |
| **`buildPath`** | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
| **`components`** | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |
| **`custom`** | Any custom properties you wish to pass to the Admin Panel. |
| **`dateFormat`** | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
| **`disable`** | If set to `true`, the entire Admin Panel will be disabled. |
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
| **`meta`** | Base metadata to use for the Admin Panel. [More details](./metadata). |
| **`routes`** | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
| **`user`** | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
<Banner type="success">
<strong>Reminder:</strong>
These are the _root-level_ options for the Admin Panel. You can also customize [Collection Admin Options](./collections) and [Global Admin Options](./globals) through their respective `admin` keys.
</Banner>
### The Admin User Collection
<Banner type="warning">
<strong>Important:</strong>
<br />
The Payload Admin panel can only be used by one Collection that supports
[Authentication](/docs/authentication/overview).
</Banner>
To specify which Collection to use to log in to the Admin panel, pass the `admin` options a `user` key equal to the slug of the Collection that you'd like to use.
`payload.config.js`:
To specify which Collection to allow to login to the Admin Panel, pass the `admin.user` key equal to the slug of any auth-enabled Collection:
```ts
import { buildConfig } from 'payload/config'
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
user: 'admins', // highlight-line
},
})
```
By default, if you have not specified a Collection, Payload will automatically provide you with a `User` Collection which will be used to access the Admin panel. You can customize or override the fields and settings of the default `User` Collection by passing your own collection using `users` as its `slug` to Payload. When this is done, Payload will use your provided `User` Collection instead of its default version.
<Banner type="warning">
<strong>Important:</strong>
<br />
The Admin Panel can only be used by a single auth-enabled Collection. To enable authentication for a Collection, simply set `auth: true` in the Collection's configuration. See [Authentication](../authentication/overview) for more information.
</Banner>
**Note: you can use whatever Collection you'd like to access the Admin panel as long as the Collection supports Authentication. It doesn't need to be called `users`!**
By default, if you have not specified a Collection, Payload will automatically provide a `User` Collection with access to the Admin Panel. You can customize or override the fields and settings of the default `User` Collection by adding your own Collection with `slug: 'users'`. Doing this will force Payload to use your provided `User` Collection instead of its default version.
For example, you may wish to have two Collections that both support `Authentication`:
You can use whatever Collection you'd like to access the Admin Panel as long as the Collection supports [Authentication](/docs/authentication/overview). It doesn't need to be called `users`. For example, you may wish to have two Collections that both support authentication:
- `admins` - meant to have a higher level of permissions to manage your data and access the Admin panel
- `customers` - meant for end users of your app that should not be allowed to log into the Admin panel
- `admins` - meant to have a higher level of permissions to manage your data and access the Admin Panel
- `customers` - meant for end users of your app that should not be allowed to log into the Admin Panel
This is totally possible. For the above scenario, by specifying `admin: { user: 'admins' }`, your Payload Admin panel will use `admins`. Any users logged in as `customers` will not be able to log in via the Admin panel.
To do this, specify `admin: { user: 'admins' }` in your config. This will provide access to the Admin Panel to only `admins`. Any users authenticated as `customers` will be prevented from accessing the Admin Panel. See [Access Control](/docs/access-control/overview) for full details.
### Light and dark modes
### Role-based Access Control
Users in the admin panel have access to choosing between light mode and dark mode for their editing experience. The setting is managed while logged into the admin UI within the user account page and will be stored with the browser. By default, the operating system preference is detected and used.
It is also possible to allow multiple user types into the Admin Panel with limited permissions, known as role-based access control (RBAC). For example, you may wish to have two roles within the `admins` Collection:
### Restricting user access
- `super-admin` - full access to the Admin Panel to perform any action
- `editor` - limited access to the Admin Panel to only manage content
If you would like to restrict which users from a single Collection can access the Admin panel, you can use the `admin` access control function. [Click here](/docs/access-control/overview#admin) to learn more.
To do this, add a `roles` or similar field to your auth-enabled Collection, then use the `access.admin` property to grant or deny access based on the value of that field. See [Access Control](/docs/access-control/overview) for full details. For a complete, working example of role-based access control, check out the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth/payload).
## Customizing Routes
You have full control over the routes that Payload binds itself to. This includes both [Root-level Routes](#root-level-routes) such as the [REST API](../rest-api/overview), and [Admin-level Routes](#admin-level-routes) such as the user's account page. You can customize these routes to meet the needs of your application simply by specifying the desired paths in your config.
### Root-level Routes
Root-level routes are those that are not behind the `/admin` path, such as the [REST API](../rest-api/overview) and [GraphQL API](../graphql/overview), or the root path of the Admin Panel itself.
To customize root-level routes, use the `routes` property in your [Payload Config](../configuration/overview):
| `api` | `/api` | The [REST API](../rest-api/overview) base path. |
| `graphQL` | `/graphql` | The [GraphQL API](../graphql/overview) base path. |
| `graphQLPlayground` | `/graphql-playground` | The GraphQL Playground. |
<Banner type="success">
<strong>Tip:</strong>
You can easily add _new_ routes to the Admin Panel through [Custom Endpoints](../rest-api/overview#custom-endpoints) and [Custom Views](./views).
</Banner>
#### Customizing Root-level Routes
You can change the Root-level Routes as needed, such as to mount the Admin Panel at the root of your application.
Changing Root-level Routes also requires a change to [Project Structure](#project-structure) to match the new route. For example, if you set `routes.admin` to `/`, you would need to completely remove the `admin` directory from the project structure:
```plaintext
app/
├─ (payload)/
├── [[...segments]]/
├──── ...
```
<Banner type="warning">
<strong>Note:</strong>
If you set Root-level Routes _before_ auto-generating the Admin Panel, your [Project Structure](#project-structure) will already be set up correctly.
</Banner>
### Admin-level Routes
Admin-level routes are those behind the `/admin` path. These are the routes that are part of the Admin Panel itself, such as the user's account page, the login page, etc.
To customize admin-level routes, use the `admin.routes` property in your [Payload Config](../configuration/overview):
| `account` | `/account` | The user's account page. |
| `createFirstUser` | `/create-first-user` | The page to create the first user. |
| `forgot` | `/forgot` | The password reset page. |
| `inactivity` | `/logout-inactivity` | The page to redirect to after inactivity. |
| `login` | `/login` | The login page. |
| `logout` | `/logout` | The logout page. |
| `reset` | `/reset` | The password reset page. |
| `unauthorized` | `/unauthorized` | The unauthorized page. |
<Banner type="success">
<strong>Note:</strong>
You can also swap out entire _views_ out for your own, using the `admin.views` property of the Payload Config. See [Custom Views](./views) for more information.
</Banner>
## I18n
The Payload Admin Panel is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/beta/packages/translations). Languages are automatically detected based on the user's browser and used by the Admin Panel to display all text in that language. If no language was detected, or if the user's language is not yet supported, English will be chosen. Users can easily specify their language by selecting one from their account page. See [I18n](../configuration/i18n) for more information.
## Light and Dark Modes
Users in the Admin Panel have the ability to choose between light mode and dark mode for their editing experience. Users can select their preferred theme from their account page. Once selected, it is saved to their user's preferences and persisted across sessions and devices. If no theme was selected, the Admin Panel will automatically detect the operation system's theme and use that as the default.
As your users interact with your Admin panel, you might want to store their preferences in a persistent manner, so that when they revisit the Admin panel, they can pick right back up where they left off.
As your users interact with the [Admin Panel](./overview), you might want to store their preferences in a persistent manner, so that when they revisit the Admin Panel in a different session or from a different device, they can pick right back up where they left off.
Out of the box, Payload handles the persistence of your users' preferences in a handful of ways, including:
1. Collection `List` view active columns, and their order, that users define
1. Their last active locale
1. The "collapsed" state of blocks, on a document level, as users edit or interact with documents
1. Columns in the Collection List View: their active state and order
1. The user's last active [Locale](../configuration/localization)
1. The "collapsed" state of `blocks`, `array`, and `collapsible` fields
1. The last-known state of the `Nav` component, etc.
<Banner type="warning">
<strong>Important:</strong>
@@ -21,38 +22,38 @@ Out of the box, Payload handles the persistence of your users' preferences in a
that is reading or setting a preference via all provided authentication methods.
</Banner>
### Use cases
## Use Cases
This API is used significantly for internal operations of the Admin panel, as mentioned above. But, if you're building your own React components for use in the Admin panel, you can allow users to set their own preferences in correspondence to their usage of your components. For example:
This API is used significantly for internal operations of the Admin Panel, as mentioned above. But, if you're building your own React components for use in the Admin Panel, you can allow users to set their own preferences in correspondence to their usage of your components. For example:
- If you have built a "color picker", you could "remember" the last used colors that the user has set for easy access next time
- If you've built a custom `Nav` component, and you've built in an "accordion-style" UI, you might want to store the `collapsed` state of each Nav collapsible item. This way, if an editor returns to the panel, their `Nav` state is persisted automatically
- You might want to store `recentlyAccessed` documents to give admin editors an easy shortcut back to their recently accessed documents on the `Dashboard` or similar
- Many other use cases exist. Invent your own! Give your editors an intelligent and persistent editing experience.
### Database
## Database
Payload automatically creates an internally used `payload-preferences` collection that stores user preferences. Each document in the `payload-preferences` collection contains the following shape:
Payload automatically creates an internally used `payload-preferences` Collection that stores user preferences. Each document in the `payload-preferences` Collection contains the following shape:
| `id` | A unique ID for each preference stored. |
| `key` | A unique `key` that corresponds to the preference. |
| `user.value` | The ID of the `user` that is storing its preference. |
| `user.relationTo` | The `slug` of the collection that the `user` is logged in as. |
| `user.relationTo` | The `slug` of the Collection that the `user` is logged in as. |
| `value` | The value of the preference. Can be any data shape that you need. |
| `createdAt` | A timestamp of when the preference was created. |
| `updatedAt` | A timestamp set to the last time the preference was updated. |
### APIs
## APIs
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#) APIs.
### Adding or reading Preferences in your own components
## Adding or reading Preferences in your own components
The Payload admin panel offers a `usePreferences` hook. The hook is only meant for use within the admin panel itself. It provides you with two methods:
The Payload Admin Panel offers a `usePreferences` hook. The hook is only meant for use within the Admin Panel itself. It provides you with two methods:
##### `getPreference`
#### `getPreference`
This async method provides an easy way to retrieve a user's preferences by `key`. It will return a promise containing the resulting preference value.
@@ -60,7 +61,7 @@ This async method provides an easy way to retrieve a user's preferences by `key`
- `key`: the `key` of your preference to retrieve.
##### `setPreference`
#### `setPreference`
Also async, this method provides you with an easy way to set a user preference. It returns `void`.
@@ -71,11 +72,12 @@ Also async, this method provides you with an easy way to set a user preference.
## Example
Here is an example for how you can utilize `usePreferences` within your custom Admin panel components. Note - this example is not fully useful and is more just a reference for how to utilize the Preferences API. In this case, we are demonstrating how to set and retrieve a user's last used colors history within a `ColorPicker` or similar type component.
Here is an example for how you can utilize `usePreferences` within your custom Admin Panel components. Note - this example is not fully useful and is more just a reference for how to utilize the Preferences API. In this case, we are demonstrating how to set and retrieve a user's last used colors history within a `ColorPicker` or similar type component.
```
```tsx
'use client'
import React, { Fragment, useState, useEffect, useCallback } from 'react';
import { usePreferences } from 'payload/components/preferences';
Views are the individual pages that make up the [Admin Panel](./overview), such as the Dashboard, List, and Edit views. One of the most powerful ways to customize the Admin Panel is to create Custom Views. These are [Custom Components](./components) that can either replace built-in views or can be entirely new.
There are four types of views within the Admin Panel:
- [Root Views](#root-views)
- [Collection Views](#collection-views)
- [Global Views](#global-views)
- [Document Views](#document-views)
To swap in your own Custom Views, consult the list of available components. Determine the scope that corresponds to what you are trying to accomplish, then [author your React component(s)](#building-custom-views) accordingly.
## Root Views
Root Views are the main views of the [Admin Panel](./overview). These are views that are scoped directly under the `/admin` route, such as the Dashboard or Account views.
To easily swap Root Views with your own, or to [create entirely new ones](#adding-new-root-views), use the `admin.components.views` property of your root [Payload Config](../configuration/overview):
Your Custom Root Views can optionally use one of the templates that Payload provides. The most common of these is the Default Template which provides the basic layout and navigation. Here is an example of what that might look like:
```tsx
import type { AdminViewProps } from 'payload'
import { DefaultTemplate } from '@payloadcms/next/templates'
| **`Component`** \* | Pass in the component path that should be rendered when a user navigates to this route. |
| **`path`** \* | Any valid URL path or array of paths that [`path-to-regexp`](https://www.npmjs.com/package/path-to-regex) understands. |
| **`exact`** | Boolean. When true, will only match if the path matches the `usePathname()` exactly. |
| **`strict`** | When true, a path that has a trailing slash will only match a `location.pathname` with a trailing slash. This has no effect when there are additional URL segments in the pathname. |
| **`sensitive`** | When true, will match if the path is case sensitive.
| **`meta`** | Page metadata overrides to apply to this view within the Admin Panel. [More details](./metadata). |
_\* An asterisk denotes that a property is required._
### Adding New Views
To add a _new_ views to the [Admin Panel](./overview), simply add your own key to the `views` object with at least a `path` and `Component` property. For example:
The above example shows how to add a new [Root View](#root-views), but the pattern is the same for [Collection Views](#collection-views), [Global Views](#global-views), and [Document Views](#document-views). For help on how to build your own Custom Views, see [Building Custom Views](#building-custom-views).
<Banner type="warning">
<strong>Note:</strong>
<br />
Routes are cascading, so unless explicitly given the `exact` property, they will
match on URLs that simply _start_ with the route's path. This is helpful when creating catch-all
routes in your application. Alternatively, define your nested route _before_ your parent
route.
</Banner>
## Collection Views
Collection Views are views that are scoped under the `/collections` route, such as the Collection List and Document Edit views.
To easily swap out Collection Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views` property of your [Collection Config](../collections/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._
<Banner type="warning">
<strong>Note:</strong>
The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.
| **`edit`** | The Edit View is used to edit a single document for any given Collection. [More details](#document-views). |
| **`list`** | The List View is used to show a list of documents for any given Collection. |
<Banner type="success">
<strong>Note:</strong>
You can also add _new_ Collection Views to the config by adding a new key to the `views` object with at least a `path` and `Component` property. See [Adding New Views](#adding-new-views) for more information.
</Banner>
## Global Views
Global Views are views that are scoped under the `/globals` route, such as the Document Edit View.
To easily swap out Global Views with your own or [create entirely new ones](#adding-new-views), use the `admin.components.views` property in your [Global Config](../globals/overview):
```ts
import type { SanitizedGlobalConfig } from 'payload'
_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._
<Banner type="warning">
<strong>Note:</strong>
The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.
| **`edit`** | The Edit View is used to edit a single document for any given Global. [More details](#document-views). |
<Banner type="success">
<strong>Note:</strong>
You can also add _new_ Global Views to the config by adding a new key to the `views` object with at least a `path` and `Component` property. See [Adding New Views](#adding-new-views) for more information.
</Banner>
## Document Views
Document Views are views that are scoped under the `/collections/:collectionSlug/:id` or the `/globals/:globalSlug` route, such as the Edit View or the API View. All Document Views keep their overall structure across navigation changes, such as their title and tabs, and replace only the content below.
To easily swap out Document Views with your own, or to [create entirely new ones](#adding-new-document-views), use the `admin.components.views.Edit[key]` property in your [Collection Config](../collections/overview) or [Global Config](../globals/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._
<Banner type="warning">
<strong>Note:</strong>
If you need to replace the _entire_ Edit View, including _all_ nested Document Views, use the `root` key. See [Custom Collection Views](#collection-views) or [Custom Global Views](#global-views) for more information.
| **`root`** | The Root View overrides all other nested views and routes. No document controls or tabs are rendered when this key is set. |
| **`default`** | The Default View is the primary view in which your document is edited. It is rendered within the "Edit" tab. |
| **`versions`** | The Versions View is used to navigate the version history of a single document. It is rendered within the "Versions" tab. [More details](../versions). |
| **`version`** | The Version View is used to edit a single version of a document. It is rendered within the "Version" tab. [More details](../versions). |
| **`api`** | The API View is used to display the REST API JSON response for a given document. It is rendered within the "API" tab. |
| **`livePreview`** | The LivePreview view is used to display the Live Preview interface. It is rendered within the "Live Preview" tab. [More details](../live-preview). |
### Document Tabs
Each Document View can be given a new tab in the Edit View, if desired. Tabs are highly configurable, from as simple as changing the label to swapping out the entire component, they can be modified in any way. To add or customize tabs in the Edit View, use the `tab` key:
```ts
import type { SanitizedCollectionConfig } from 'payload'
Custom Views are just [Custom Components](./components) rendered at the page-level. To understand how to build Custom Views, first review the [Building Custom Components](./components#building-custom-components) guide. Once you have a Custom Component ready, you can use it as a Custom View.
```ts
import type { SanitizedCollectionConfig } from 'payload'
| **`initPageResult`** | An object containing `req`, `payload`, `permissions`, etc. |
| **`clientConfig`** | The Client Config object. [More details](../components#accessing-the-payload-config). |
| **`importMap`** | The import map object. |
| **`params`** | An object containing the [Dynamic Route Parameters](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes). |
| **`searchParams`** | An object containing the [Search Parameters](https://developer.mozilla.org/docs/Learn/Common_questions/What_is_a_URL#parameters). |
<Banner type="success">
<strong>Reminder:</strong>
All [Custom Server Components](./components) receive `payload` and `i18n` by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
<Banner type="warning">
<strong>Important:</strong>
It's up to you to secure your custom views. If your view requires a user to be logged in or to
have certain access rights, you should handle that within your view component yourself.
The Vite bundler is currently in beta. If you would like to help us test this package, we'd love to hear from you if you find any [bugs or issues](https://github.com/payloadcms/payload/issues/)!
</Banner>
Payload has a Vite bundler that you can install and bundle the Admin Panel with. This is an alternative to the [Webpack](/docs/admin/webpack) bundler and might give some performance boosts to your development workflow.
To use Vite as your bundler, first you need to install the package:
```bash
yarn add @payloadcms/bundler-vite
```
Then you will need to add the [bundler](/docs/admin/bundlers) to your Payload config:
```ts
import { buildConfig } from '@payloadcms/config'
import { viteBundler } from '@payloadcms/bundler-vite'
export default buildConfig({
collections: [],
admin: {
bundler: viteBundler(),
}
})
```
Vite works fundamentally differently than Webpack. In development mode, it will first pre-bundle any of your dependencies that are CommonJS-only, and then it'll leverage ESM directly in your browser for a better HMR experience.
It then uses Rollup to create production builds of your admin UI. With Vite, you should see a decent performance boost—especially after your first cold start. However, that first cold start might take a few more seconds.
<Banner type="warning">
In most cases, Vite should work out of the box. But existing Payload plugins may need to make compatibility changes to support Vite.
</Banner>
This is because Vite aliases work fundamentally differently than Webpack aliases, and Payload relies on aliasing server-only code out of the Payload config to ensure that the bundled admin JS works within your browser.
Here are the main differences between how Vite aliases work and how Webpack aliases work.
**Vite aliases do not work with absolute paths.**
In Vite, alias keys must <strong>exactly match</strong> a import paths. If you have 2 files that import the same server-only module, but have different import paths, you would need to add 2 aliases to support both import paths.
```ts
// File A
import serverOnlyModule from '../server-only-module'
// File B
import serverOnlyModule from '../../server-only-module'
// payload.config.ts
// You would need to add 2 aliases to support both import paths
**Vite aliases do not get applied to pre-bundled dependencies.**
This especially affects plugins, as plugins will be pre-bundled by Vite using `esbuild`. To get around this and support Vite, plugin authors need to configure an alias to their plugin at the top level, so that the alias will work accordingly.
Here's an example. Say your plugin is called `payload-plugin-cool`. It's imported as follows:
```ts
import { myCoolPlugin } from 'payload-plugin-cool'
```
That plugin should create an alias to support Vite as follows:
This will effectively alias the entire plugin and work with Vite. If the plugin requires admin-specific code, then the `./my-admin-plugin.js` alias target file should reflect any changes necessary to the admin UI that the main server-side plugin performs.
### Extending the Vite config
The Payload config supports a new property for plugins to be able to extend the Vite config specifically. That property exists on the main Payload config under `admin.vite`. You can check out the [Vite docs](https://vitejs.dev/config/shared-options.html) for more information on what you can do with the Vite config.
It's a function that takes a Vite config, and returns an updated Vite config. Here's an example:
Learn more about [aliasing server-only modules](https://payloadcms.com/docs/admin/excluding-server-code#aliasing-server-only-modules).
Even though there is a new property for Vite configs specifically, we have implemented some "compatibility" between Webpack and Vite out-of-the-box.
If your config specifies Webpack aliases, we attempt to leverage them automatically within the Vite config. They are merged into the Vite alias configuration seamlessly and may work out-of-the-box.
desc: The Payload admin panel uses Webpack 5 and supports many common functionalities such as SCSS and Typescript out of the box to give you more freedom.
Payload has a Webpack (v5) bundler that you can build the Admin panel with. For now, we recommended using it because it is stable. If you are feeling a bit more adventurous you can give the [Vite](/docs/admin/vite) bundler a shot.
Out of the box, the Webpack bundler supports common functionalities such as SCSS and Typescript, but there are many cases where you may need to add support for additional functionalities.
#### Installation
```bash
yarn add @payloadcms/bundler-webpack
```
#### Import the bundler
```ts
// payload.config.ts
import { buildConfig } from 'payload/config'
import { webpackBundler } from '@payloadcms/bundler-webpack'
export default buildConfig({
// highlight-start
admin: {
bundler: webpackBundler()
},
// highlight-end
})
```
### Extending Webpack
If you need to extend the Webpack config, you can do so by passing a function to the `admin.webpack` property on your Payload config.
The function will receive the Webpack config as an argument and should return the modified config.
```ts
// payload.config.ts
import { buildConfig } from 'payload/config'
import { webpackBundler } from '@payloadcms/bundler-webpack'
export default buildConfig({
admin: {
bundler: webpackBundler()
// highlight-start
webpack: (config) => {
// full control of the Webpack config
return config
},
// highlight-end
},
})
```
<Banner type="success">
<strong>Tip:</strong>
<br />
If changes to your Webpack aliases are not surfacing, they might be
[cached](https://webpack.js.org/configuration/cache/) in `node_modules/.cache/webpack`. Try
To integrate with third-party APIs or services, you might need the ability to generate API keys that can be used to identify as a certain user within Payload. API keys are generated on a user-by-user basis, similar to email and passwords, and are meant to represent a single user.
For example, if you have a third-party service or external app that needs to be able to perform protected actions against Payload, first you need to create a user within Payload, i.e. `dev@thirdparty.com`. From your external application you will need to authenticate with that user, you have two options:
1. Log in each time with that user and receive an expiring token to request with.
1. Generate a non-expiring API key for that user to request with.
<Banner type="success">
<strong>Tip:</strong>
<br/>
This is particularly useful as you can create a "user" that reflects an integration with a specific external service and assign a "role" or specific access only needed by that service/integration.
</Banner>
Technically, both of these options will work for third-party integrations but the second option with API key is simpler, because it reduces the amount of work that your integrations need to do to be authenticated properly.
To enable API keys on a collection, set the `useAPIKey` auth option to `true`. From there, a new interface will appear in the [Admin Panel](../admin/overview) for each document within the collection that allows you to generate an API key for each user in the Collection.
User API keys are encrypted within the database, meaning that if your database is compromised,
your API keys will not be.
<Banner type="warning">
<strong>Important:</strong>
If you change your `PAYLOAD_SECRET`, you will need to regenerate your API keys.
<br />
The secret key is used to encrypt the API keys, so if you change the secret, existing API keys will
no longer be valid.
</Banner>
### HTTP Authentication
To authenticate REST or GraphQL API requests using an API key, set the `Authorization` header. The header is case-sensitive and needs the slug of the `auth.useAPIKey` enabled collection, then " API-Key ", followed by the `apiKey` that has been assigned. Payload's built-in middleware will then assign the user document to `req.user` and handle requests with the proper [Access Control](../access-control/overview). By doing this, Payload recognizes the request being made as a request by the user associated with that API key.
Payload ensures that the same, uniform [Access Control](../access-control/overview) is used across all authentication strategies. This enables you to utilize your existing Access Control configurations with both API keys and the standard email/password authentication. This consistency can aid in maintaining granular control over your API keys.
### API Key Only Auth
If you want to use API keys as the only authentication method for a collection, you can disable the default local strategy by setting `disableLocalStrategy` to `true` on the collection's `auth` property. This will disable the ability to authenticate with email and password, and will only allow for authentication via API key.
Payload's Authentication is extremely powerful and gives you everything you need when you go to build a new app or site in a secure and responsible manner.
To enable Authentication on a collection, define an `auth` property and set it to either `true` or to an object containing the options below.
| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More](/docs/authentication/config#api-keys) |
| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |
| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |
| **`lockTime`** | Set the time (in milliseconds) that a user should be locked out if they fail authentication more times than `maxLoginAttempts` allows for. |
| **`depth`** | How many levels deep a `user` document should be populated when creating the JWT and binding the `user` to the express `req`. Defaults to `0` and should only be modified if absolutely necessary, as this will affect performance. |
| **`cookies`** | Set cookie options, including `secure`, `sameSite`, and `domain`. For advanced users. |
| **`forgotPassword`** | Customize the way that the `forgotPassword` operation functions. [More](/docs/authentication/config#forgot-password) |
| **`verify`** | Set to `true` or pass an object with verification options to require users to verify by email before they are allowed to log into your app. [More](/docs/authentication/config#email-verification) |
| **`disableLocalStrategy`** | Advanced - disable Payload's built-in local auth strategy. Only use this property if you have replaced Payload's auth mechanisms with your own. |
| **`strategies`** | Advanced - an array of PassportJS authentication strategies to extend this collection's authentication with. [More](/docs/authentication/config#strategies) |
### API keys
To integrate with third-party APIs or services, you might need the ability to generate API keys that can be used to identify as a certain user within Payload.
In Payload, users are essentially documents within a collection. Just like you can authenticate as a user with an email and password, which is considered as our default local auth strategy, you can also authenticate as a user with an API key. API keys are generated on a user-by-user basis, similar to email and passwords, and are meant to represent a single user.
For example, if you have a third-party service or external app that needs to be able to perform protected actions at its discretion, you have two options:
1. Create a user for the third-party app, and log in each time to receive a token before you attempt to access any protected actions
1. Enable API key support for the Collection, where you can generate a non-expiring API key per user in the collection. This is particularly useful as you can create a "user" that reflects an integration with a specific external service and assign a "role" or specific access only needed by that service/integration. Alternatively, you could create a "super admin" user and assign an API key to that user so that any requests made with that API key are considered as being made by that super user.
Technically, both of these options will work for third-party integrations but the second option with API key is simpler, because it reduces the amount of work that your integrations need to do to be authenticated properly.
To enable API keys on a collection, set the `useAPIKey` auth option to `true`. From there, a new interface will appear in the Admin panel for each document within the collection that allows you to generate an API key for each user in the Collection.
<Banner type="success">
User API keys are encrypted within the database, meaning that if your database is compromised,
your API keys will not be.
</Banner>
<Banner type="warning">
<strong>Important:</strong>
If you change your `PAYLOAD_SECRET`, you will need to regenerate your API keys.
<br />
The secret key is used to encrypt the API keys, so if you change the secret, existing API keys will no longer be valid.
</Banner>
#### Authenticating via API Key
To authenticate REST or GraphQL API requests using an API key, set the `Authorization` header. The header is case-sensitive and needs the slug of the `auth.useAPIKey` enabled collection, then " API-Key ", followed by the `apiKey` that has been assigned. Payload's built-in middleware will then assign the user document to `req.user` and handle requests with the proper access control. By doing this, Payload recognizes the request being made as a request by the user associated with that API key.
Payload ensures that the same, uniform access control is used across all authentication strategies. This enables you to utilize your existing access control configurations with both API keys and the standard email/password authentication. This consistency can aid in maintaining granular control over your API keys.
#### API Key _Only_ Authentication
If you want to use API keys as the only authentication method for a collection, you can disable the default local strategy by setting `disableLocalStrategy` to `true` on the collection's `auth` property. This will disable the ability to authenticate with email and password, and will only allow for authentication via API key.
```ts
import { CollectionConfig } from 'payload/types'
export const Customers: CollectionConfig = {
slug: 'customers',
auth: {
useAPIKey: true,
disableLocalStrategy: true,
},
}
```
### Forgot Password
You can customize how the Forgot Password workflow operates with the following options on the `auth.forgotPassword` property:
**`generateEmailHTML`**
Function that accepts one argument, containing `{ req, token, user }`, that allows for overriding the HTML within emails that are sent to users attempting to reset their password. The function should return a string that supports HTML, which can be a full HTML email.
<Banner type="success">
<strong>Tip:</strong>
<br />
HTML templating can be used to create custom email templates, inline CSS automatically, and more.
You can make a reusable function that standardizes all email sent from Payload, which makes
sending custom emails more DRY. Payload doesn't ship with an HTML templating engine, so you are
free to choose your own.
</Banner>
Example:
```ts
import { CollectionConfig } from 'payload/types'
export const Customers: CollectionConfig = {
slug: 'customers',
auth: {
forgotPassword: {
// highlight-start
generateEmailHTML: ({ req, token, user }) => {
// Use the token provided to allow your user to reset their password
If you specify a different URL to send your users to for resetting their password, such as a page
on the frontend of your app or similar, you need to handle making the call to the Payload REST or
GraphQL reset-password operation yourself on your frontend, using the token that was provided for
you. Above, it was passed via query parameter.
</Banner>
**`generateEmailSubject`**
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
Example:
```ts
{
slug: 'customers',
auth: {
forgotPassword: {
// highlight-start
generateEmailSubject: ({ req, user }) => {
return `Hey ${user.email}, reset your password!`;
}
// highlight-end
}
}
}
```
### Email Verification
If you'd like to require email verification before a user can successfully log in, you can enable it by passing `true` or an `options` object to `auth.verify`. The following options are available:
**`generateEmailHTML`**
Function that accepts one argument, containing `{ req, token, user }`, that allows for overriding the HTML within emails that are sent to users indicating how to validate their account. The function should return a string that supports HTML, which can optionally be a full HTML email.
Example:
```ts
import { CollectionConfig } from 'payload/types'
export const Customers: CollectionConfig = {
slug: 'customers',
auth: {
verify: {
// highlight-start
generateEmailHTML: ({ req, token, user }) => {
// Use the token provided to allow your user to verify their account
return `Hey ${user.email}, verify your email by clicking here: ${url}`
},
// highlight-end
},
},
}
```
<Banner type="warning">
<strong>Important:</strong>
<br />
If you specify a different URL to send your users to for email verification, such as a page on the
frontend of your app or similar, you need to handle making the call to the Payload REST or GraphQL
verification operation yourself on your frontend, using the token that was provided for you.
Above, it was passed via query parameter.
</Banner>
**`generateEmailSubject`**
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
Example:
```ts
{
slug: 'customers',
auth: {
forgotPassword: {
// highlight-start
generateEmailSubject: ({ req, user }) => {
return `Hey ${user.email}, reset your password!`;
}
// highlight-end
}
}
}
```
### Strategies
As of Payload `1.0.0`, you can add additional authentication strategies to Payload easily by passing them to your collection's `auth.strategies` array.
Behind the scenes, Payload uses PassportJS to power its local authentication strategy, so most strategies listed on the PassportJS website will work seamlessly. Combined with adding custom components to the admin panel's `Login` view, you can create advanced authentication strategies directly within Payload.
<Banner type="warning">
This is an advanced feature, so only attempt this if you are an experienced developer. Otherwise,
just let Payload's built-in authentication handle user auth for you.
</Banner>
The `strategies` property is an array that takes objects with the following properties:
**`strategy`**
This property can accept a Passport strategy directly, or you can pass a function that takes a `payload` argument, and returns a Passport strategy.
**`name`**
If you pass a strategy to the `strategy` property directly, the `name` property is optional and allows you to override the strategy's built-in name.
However, if you pass a function to `strategy`, `name` is a required property.
In either case, Payload will prefix the strategy name with the collection `slug` that the strategy is passed to.
### Admin autologin
For testing and demo purposes you may want to skip forcing the admin user to login in order to access the panel.
The `admin.autologin` property is used to configure the how visitors are handled when accessing the admin panel.
The default is that all users will have to login and this should not be enabled for environments where data needs to protected.
Payload offers the ability to [Authenticate](./overview) via HTTP-only cookies. These can be read from the responses of `login`, `logout`, `refresh`, and `me` auth operations.
<Banner type="success">
<strong>Tip:</strong>
You can access the logged-in user from within [Access Control](../access-control/overview) and [Hooks](../hooks/overview) through the `req.user` argument. [More details](./token-data).
</Banner>
### Automatic browser inclusion
Modern browsers automatically include `http-only` cookies when making requests directly to URLs—meaning that if you are running your API on `https://example.com`, and you have logged in and visit `https://example.com/test-page`, your browser will automatically include the Payload authentication cookie for you.
### HTTP Authentication
However, if you use `fetch` or similar APIs to retrieve Payload resources from its REST or GraphQL API, you must specify to include credentials (cookies).
For more about including cookies in requests from your app to your Payload API, [read the MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Sending_a_request_with_credentials_included).
<Banner type="success">
<strong>Tip:</strong>
To make sure you have a Payload cookie set properly in your browser after logging in, you can use
the browsers Developer Tools > Application > Cookies > [your-domain-here]. The Developer tools
will still show HTTP-only cookies.
</Banner>
### CSRF Attacks
CSRF (cross-site request forgery) attacks are common and dangerous. By using an HTTP-only cookie, Payload removes many XSS vulnerabilities, however, CSRF attacks can still be possible.
For example, let's say you have a popular app `https://payload-finances.com` that allows users to manage finances, send and receive money. As Payload is using HTTP-only cookies, that means that browsers automatically will include cookies when sending requests to your domain - <strong>no matter what page created the request</strong>.
So, if a user of `https://payload-finances.com` is logged in and is browsing around on the internet, they might stumble onto a page with malicious intent. Let's look at an example:
```ts
// malicious-intent.com
// makes an authenticated request as on your behalf
In this scenario, if your cookie was still valid, malicious-intent.com would be able to make requests like the one above on your behalf. This is a CSRF attack.
### CSRF Prevention
Define domains that your trust and are willing to accept Payload HTTP-only cookie based requests from. Use the `csrf` option on the base Payload Config to do this:
```ts
// payload.config.ts
import { buildConfig } from 'payload'
const config = buildConfig({
serverURL: 'https://my-payload-instance.com',
// highlight-start
csrf: [
// whitelist of domains to allow cookie auth from
'https://your-frontend-app.com',
'https://your-other-frontend-app.com',
// `config.serverURL` is added by default if defined
This is an advanced feature, so only attempt this if you are an experienced developer. Otherwise,
just let Payload's built-in authentication handle user auth for you.
</Banner>
### Creating a strategy
At the core, a strategy is a way to authenticate a user making a request. As of `3.0` we moved away from [Passport](https://www.passportjs.org) in favor of pulling back the curtain and putting you in full control.
| **`headers`** \* | The headers on the incoming request. Useful for retrieving identifiable information on a request. |
| **`payload`** \* | The Payload class. Useful for authenticating the identifiable information against Payload. |
| **`isGraphQL`** | Whether or not the request was made from a GraphQL endpoint. Default is `false`. |
### Example Strategy
At its core a strategy simply takes information from the incoming request and returns a user. This is exactly how Payload's built-in strategies function.
Your `authenticate` method should return an object containing a Payload user document and any optional headers that you'd like Payload to set for you when we return a response.
```ts
import { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
slug: 'users',
auth: {
disableLocalStrategy: true,
// highlight-start
strategies: [
{
name: 'custom-strategy',
authenticate: ({ payload, headers }) => {
const usersQuery = await payload.find({
collection: 'users',
where: {
code: {
equals: headers.get('code'),
},
secret: {
equals: headers.get('secret'),
},
},
})
return {
// Send the user back to authenticate,
// or send null if no user should be authenticated
desc: Email Verification allows users to verify their email address before they're account is fully activated. Email Verification ties directly into the Email functionality that Payload provides.
[Authentication](./overview) ties directly into the [Email](../email) functionality that Payload provides. This allows you to send emails to users for verification, password resets, and more. While Payload provides default email templates for these actions, you can customize them to fit your brand.
## Email Verification
Email Verification forces users to prove they have access to the email address they can authenticate. This will help to reduce spam accounts and ensure that users are who they say they are.
To enable Email Verification, use the `auth.verify` property on your [Collection Config](../configuration/collections):
```ts
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
verify: true // highlight-line
},
}
```
<Banner type="info">
<strong>Tip:</strong>
Verification emails are fully customizable. [More details](#generateEmailHTML).
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users indicating how to validate their account. [More details](#generateEmailHTML). |
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users indicating how to validate their account. [More details](#generateEmailSubject). |
#### generateEmailHTML
Function that accepts one argument, containing `{ req, token, user }`, that allows for overriding the HTML within emails that are sent to users indicating how to validate their account. The function should return a string that supports HTML, which can optionally be a full HTML email.
```ts
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
verify: {
// highlight-start
generateEmailHTML: ({ req, token, user }) => {
// Use the token provided to allow your user to verify their account
return `Hey ${user.email}, verify your email by clicking here: ${url}`
},
// highlight-end
},
},
}
```
<Banner type="warning">
<strong>Important:</strong>
If you specify a different URL to send your users to for email verification, such as a page on the
frontend of your app or similar, you need to handle making the call to the Payload REST or GraphQL
verification operation yourself on your frontend, using the token that was provided for you.
Above, it was passed via query parameter.
</Banner>
#### generateEmailSubject
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
```ts
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
verify: {
// highlight-start
generateEmailSubject: ({ req, user }) => {
return `Hey ${user.email}, reset your password!`;
}
// highlight-end
}
}
}
```
## Forgot Password
You can customize how the Forgot Password workflow operates with the following options on the `auth.forgotPassword` property:
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users attempting to reset their password. [More details](#generateEmailHTML). |
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users attempting to reset their password. [More details](#generateEmailSubject). |
#### generateEmailHTML
This function allows for overriding the HTML within emails that are sent to users attempting to reset their password. The function should return a string that supports HTML, which can be a full HTML email.
```ts
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
forgotPassword: {
// highlight-start
generateEmailHTML: ({ req, token, user }) => {
// Use the token provided to allow your user to reset their password
| `token` | The token that is generated for the user to reset their password. |
| `user` | The user document that is attempting to reset their password. |
#### generateEmailSubject
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
```ts
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
auth: {
forgotPassword: {
// highlight-start
generateEmailSubject: ({ req, user }) => {
return `Hey ${user.email}, reset your password!`;
}
// highlight-end
}
}
}
```
The following arguments are passed to the `generateEmailSubject` function:
Payload offers the ability to [Authenticate](./overview) via JSON Web Tokens (JWT). These can be read from the responses of `login`, `logout`, `refresh`, and `me` auth operations.
<Banner type="success">
<strong>Tip:</strong>
You can access the logged-in user from within [Access Control](../access-control/overview) and [Hooks](../hooks/overview) through the `req.user` argument. [More details](./token-data).
</Banner>
### Identifying Users Via The Authorization Header
In addition to authenticating via an HTTP-only cookie, you can also identify users via the `Authorization` header on an HTTP request.
Example:
```ts
const user = await fetch('http://localhost:3000/api/users/login', {
In some cases you may want to prevent the token from being returned from the auth operations. You can do that by setting `removeTokenFromResponse` to `true` like so:
Enabling Authentication on a Collection automatically exposes additional auth-based operations in the Local, REST, and GraphQL APIs.
Enabling [Authentication](./overview) on a [Collection](../configuration/collections) automatically exposes additional auth-based operations in the [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview).
### Access
## Access
The Access operation returns what a logged in user can and can't do with the collections and globals that are registered via your config. This data can be immensely helpful if your app needs to show and hide certain features based on access control, as the Payload Admin panel does.
The Access operation returns what a logged in user can and can't do with the collections and globals that are registered via your config. This data can be immensely helpful if your app needs to show and hide certain features based on [Access Control](../access-control/overview), just as the [Admin Panel](../admin/overview) does.
**REST API endpoint**:
@@ -69,7 +69,7 @@ query {
Document access can also be queried on a collection/global basis. Access on a global can queried like `http://localhost:3000/api/global-slug/access`, Collection document access can be queried like `http://localhost:3000/api/collection-slug/access/:id`.
### Me
## Me
Returns either a logged in user with token or null when there is no logged in user.
@@ -105,9 +105,9 @@ query {
}
```
### Login
## Login
Accepts an `email` and `password`. On success, it will return the logged in user as well as a token that can be used to authenticate. In the GraphQL and REST APIs, this operation also automatically sets an HTTP-only cookie including the user's token. If you pass an Express `res` to the Local API operation, Payload will set a cookie there as well.
Accepts an `email` and `password`. On success, it will return the logged in user as well as a token that can be used to authenticate. In the GraphQL and REST APIs, this operation also automatically sets an HTTP-only cookie including the user's token. If you pass a `res` to the Local API operation, Payload will set a cookie there as well.
**Example REST API login**:
@@ -166,7 +166,7 @@ const result = await payload.login({
})
```
### Logout
## Logout
As Payload sets HTTP-only cookies, logging out cannot be done by just removing a cookie in JavaScript, as HTTP-only cookies are inaccessible by JS within the browser. So, Payload exposes a `logout` operation to delete the token in a safe way.
@@ -189,9 +189,9 @@ mutation {
}
```
### Refresh
## Refresh
Allows for "refreshing" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by sending the operation the token that is about to expire.
Allows for "refreshing" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by executing this operation via the authenticated user.
This operation requires a non-expired token to send back a new one. If the user's token has already expired, you will need to allow them to log in again to retrieve a new token.
@@ -237,14 +237,7 @@ mutation {
}
```
<Banner type="success">
The Refresh operation will automatically find the user's token in either a JWT header or the
HTTP-only cookie. But, you can specify the token you're looking to refresh by providing the REST
API with a `token` within the JSON body of the request, or by providing the GraphQL resolver a
`token` arg.
</Banner>
### Verify by Email
## Verify by Email
If your collection supports email verification, the Verify operation will be exposed which accepts a verification token and sets the user's `_verified` property to `true`, thereby allowing the user to authenticate with the Payload API.
@@ -276,11 +269,11 @@ const result = await payload.verifyEmail({
})
```
### Unlock
## Unlock
If a user locks themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The Admin panel features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation.
If a user locks themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The [Admin Panel](../admin/overview) features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation.
To restrict who is allowed to unlock users, you can utilize the [`unlock`](/docs/access-control/overview#unlock) access control function.
To restrict who is allowed to unlock users, you can utilize the [`unlock`](../access-control/overview#unlock) access control function.
**Example REST API unlock**:
@@ -309,13 +302,13 @@ const result = await payload.unlock({
})
```
### Forgot Password
## Forgot Password
Payload comes with built-in forgot password functionality. Submitting an email address to the Forgot Password operation will generate an email and send it to the respective email address with a link to reset their password.
The link to reset the user's password contains a token which is what allows the user to securely reset their password.
By default, the Forgot Password operations send users to the Payload Admin panel to reset their password, but you can customize the generated email to send users to the frontend of your app instead by [overriding the email HTML](/docs/authentication/config#forgot-password).
By default, the Forgot Password operations send users to the [Admin Panel](../admin/overview) to reset their password, but you can customize the generated email to send users to the frontend of your app instead by [overriding the email HTML](/docs/authentication/overview#forgot-password).
After a user has "forgotten" their password and a token is generated, that token can be used to send to the reset password operation along with a new password which will allow the user to reset their password securely.
desc: Payload provides highly secure user Authentication out of the box, and you can fully customize, override, or remove the default Authentication support.
title="Simplified Authentication for Headless CMS: Unlocking Reusability in One Line"
/>
<Banner>
Payload provides for highly secure and customizable user Authentication out of the box, which
allows for users to identify themselves to Payload.
</Banner>
Authentication is a critical part of any application. Payload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the [Admin Panel](../admin/overview), all well as your own external applications, completely eliminating the need for paid, third-party platforms and services.
Authentication is used within the Payload Admin panel itself as well as throughout your app(s) themselves however you determine necessary.
Here are some common use cases of Authentication in your own applications:
_Admin panel screenshot depicting an Admins Collection with Auth enabled_
- Customer accounts for an e-commerce app
- User accounts for a SaaS product
- P2P apps or social sites where users need to log in and manage their profiles
- Online games where players need to track their progress over time
**Here are some common use cases of Authentication outside of Payload's dashboard itself:**
When Authentication is enabled on a [Collection](../configuration/collections), Payload injects all necessary functionality to support the entire user flow. This includes all [auth-related operations](./operations) like account creation, logging in and out, and resetting passwords, all [auth-related emails](./email) like email verification and password reset, as well as any necessary UI to manage users from the Admin Panel.
- Customer accounts for an ecommerce app
- Customer accounts for a SaaS product
- P2P app or social site where users need to log in and manage their profiles
- Online game where players need to track their progress over time
By default, Payload provides you with a `User` collection that supports Authentication, which is used to access the Admin panel. But, you can add support to one or many Collections of your own. For more information on how to customize, override, or remove the default `User` collection, [click here](/docs/admin/overview#the-admin-user-collection).
### Enabling Auth on a collection
Every Payload Collection can opt-in to supporting Authentication by specifying the `auth` property on the Collection's config to either `true` or to an object containing `auth` options.
**For a full list of all `auth` options, [click here](/docs/authentication/config).**
Simple example collection:
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collection#auth):
_Admin Panel screenshot depicting an Admins Collection with Auth enabled_
## Config Options
Any [Collection](../configuration/collections) can opt-in to supporting Authentication. Once enabled, each Document that is created within the Collection can be thought of as a "user". This enables a complete authentication workflow on your Collection, such as logging in and out, resetting their password, and more.
<Banner type="warning">
<strong>Note:</strong>
By default, Payload provides an auth-enabled `User` Collection which is used to access the Admin Panel. [More details](../admin/overview#the-admin-user-collection).
</Banner>
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections):
```ts
import { CollectionConfig } from 'payload'
export const Admins: CollectionConfig = {
slug: 'admins',
// ...
// highlight-start
auth: {
tokenExpiration: 7200, // How many seconds to keep the user logged in
**By enabling Authentication on a config, the following modifications will automatically be made to your Collection:**
1. `email` as well as password `salt` & `hash` fields will be added to your Collection's schema
1. The Admin panel will feature a new set of corresponding UI to allow for changing password and editing email
1. [A new set of `operations`](/docs/authentication/operations) will be exposed via Payload's REST, Local, and GraphQL APIs
Once enabled, each document that is created within the Collection can be thought of as a `user` - who can make use of commonly required authentication functions such as logging in / out, resetting their password, and more.
### Logging in / out, resetting password, etc.
[Click here](/docs/authentication/operations) for a list of all automatically-enabled Auth operations, including `login`, `logout`, `refresh`, and others.
### Token-based auth
Successfully logging in returns a `JWT` (JSON web token) which is how a user will identify themselves to Payload. By providing this JWT via either an HTTP-only cookie or an `Authorization: JWT` or `Authorization: Bearer` header, Payload will automatically identify the user and add its user JWT data to the Express `req`, which is available throughout Payload including within access control, hooks, and more.
You can specify what data gets encoded to the JWT token by setting `saveToJWT` to true in your auth collection fields. If you wish to use a different key other than the field `name`, you can provide it to `saveToJWT` as a string. It is also possible to use `saveToJWT` on fields that are nested in inside groups and tabs. If a group has a `saveToJWT` set it will include the object with all sub-fields in the token. You can set `saveToJWT: false` for any fields you wish to omit. If a field inside a group has `saveToJWT` set, but the group does not, the field will be included at the top level of the token.
<Banner type="success">
<Banner type="info">
<strong>Tip:</strong>
<br />
You can access the logged-in user from access control functions and hooks via the Express{' '}
<strong>req</strong>. The logged-in user is automatically added as the <strong>user</strong>{' '}
property.
For default auth behavior, set `auth: true`. This is a good starting point for most applications.
</Banner>
### HTTP-only cookies
Payload `login`, `logout`, and `refresh` operations make use of HTTP-only cookies for authentication purposes. HTTP-only cookies are a highly secure method of storing identifiable data on a user's device so that Payload can automatically recognize a returning user until their cookie expires. They are totally protected from common XSS attacks and cannot be read at all via JavaScript in the browser.
##### Automatic browser inclusion
Modern browsers automatically include `http-only` cookies when making requests directly to URLs—meaning that if you are running your API on http://example.com, and you have logged in and visit http://example.com/test-page, your browser will automatically include the Payload authentication cookie for you.
##### Using Fetch or other HTTP APIs
However, if you use `fetch` or similar APIs to retrieve Payload resources from its REST or GraphQL API, you need to specify to include credentials (cookies).
For more about how to automatically include cookies in requests from your app to your Payload API, [click here](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Sending_a_request_with_credentials_included).
<Banner type="success">
<strong>Tip:</strong>
<br />
To make sure you have a Payload cookie set properly in your browser after logging in, you can use
Chrome's Developer Tools - Application - Cookies - [your-domain-here]. The Chrome Developer tools
will still show HTTP-only cookies, even when JavaScript running on the page can't.
</Banner>
### CSRF Protection
CSRF (cross-site request forgery) attacks are common and dangerous. By using an HTTP-only cookie, Payload removes many XSS vulnerabilities, however, CSRF attacks can still be possible.
For example, let's say you have a very popular app running at coolsite.com. This app allows users to manage finances and send / receive money. As Payload is using HTTP-only cookies, that means that browsers automatically will include cookies when sending requests to your domain - no matter what page created the request.
So, if a user of coolsite.com is logged in and just browsing around on the internet, they might stumble onto a page with bad intentions. That bad page might automatically make requests to all sorts of sites to see if they can find one that they can log into - and coolsite.com might be on their list. If your user was logged in while they visited that evil site, the attacker could do whatever they wanted as if they were your coolsite.com user by just sending requests to the coolsite API (which would automatically include the auth cookie). They could send themselves a bunch of money from your user's account, change the user's password, etc. This is what a CSRF attack is.
<Banner type="warning">
<strong>
To protect against CSRF attacks, Payload only accepts cookie-based authentication from domains
that you explicitly whitelist.
</strong>
<strong>Note:</strong>
Auth-enabled Collections with be automatically injected with the `hash`, `salt`, and `email` fields. [More details](../fields/overview#field-names).
</Banner>
To define domains that should allow users to identify themselves via the Payload HTTP-only cookie, use the `csrf` option on the base Payload config to whitelist domains that you trust.
| **`cookies`** | Set cookie options, including `secure`, `sameSite`, and `domain`. For advanced users. |
| **`depth`** | How many levels deep a `user` document should be populated when creating the JWT and binding the `user` to the `req`. Defaults to `0` and should only be modified if absolutely necessary, as this will affect performance. |
| **`disableLocalStrategy`** | Advanced - disable Payload's built-in local auth strategy. Only use this property if you have replaced Payload's auth mechanisms with your own. |
| **`forgotPassword`** | Customize the way that the `forgotPassword` operation functions. [More details](./email#forgot-password). |
| **`lockTime`** | Set the time (in milliseconds) that a user should be locked out if they fail authentication more times than `maxLoginAttempts` allows for. |
| **`loginWithUsername`** | Ability to allow users to login with username/password. [More](/docs/authentication/overview#login-with-username) |
| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |
| **`removeTokenFromResponses`** | Set to true if you want to remove the token from the returned authentication API responses such as login or refresh. |
| **`strategies`** | Advanced - an array of custom authentification strategies to extend this collection's authentication with. [More details](./custom-strategies). |
| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |
| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More details](./api-keys). |
| **`verify`** | Set to `true` or pass an object with verification options to require users to verify by email before they are allowed to log into your app. [More details](./email#email-verification). |
```ts
import { buildConfig } from 'payload/config'
### Login With Username
const config = buildConfig({
collections: [
// collections here
],
// highlight-start
csrf: [
// whitelist of domains to allow cookie auth from
'https://your-frontend-app.com',
'https://your-other-frontend-app.com',
],
// highlight-end
})
export default config
```
### Identifying users via the Authorization Header
In addition to authenticating via an HTTP-only cookie, you can also identify users via the `Authorization` header on an HTTP request.
You can allow users to login with their username instead of their email address by setting the `loginWithUsername` property to `true`.
Or, you can pass an object with additional options:
```ts
{
slug: 'customers',
auth: {
loginWithUsername: {
allowEmailLogin: true, // default: false
requireEmail: false, // default: false
},
},
}
```
**`allowEmailLogin`**
If set to `true`, users can log in with either their username or email address. If set to `false`, users can only log in with their username.
**`requireEmail`**
If set to `true`, an email address is required when creating a new user. If set to `false`, email is not required upon creation.
## Auto-Login
For testing and demo purposes you may want to skip forcing the user to login in order to access your application. Typically, all users should be required to login, however, you can speed up local development time by enabling auto-login.
To enable auto-login, set the `autoLogin` property in the [Admin Config](../configuration/admin):
You can retrieve a user's token via the response to `login`, `refresh`, and `me` auth operations.
<Banner type="warning">
<strong>Warning:</strong>
The recommended way to use this feature is behind an [Environment Variable](../configuration/environment-vars). This will ensure it is _disabled_ in production.
| **`username`** | The username of the user to login as |
| **`email`** | The email address of the user to login as |
| **`password`** | The password of the user to login as. This is only needed if `prefillOnly` is set to true |
| **`prefillOnly`** | If set to true, the login credentials will be prefilled but the user will still need to click the login button. |
## Operations
All auth-related operations are available via Payload's REST, Local, and GraphQL APIs. These operations are automatically added to your Collection when you enable Authentication. [More details](./operations).
## Strategies
Out of the box Payload ships with a three powerful Authentication strategies:
- [HTTP-Only Cookies](./cookies)
- [JSON Web Tokens (JWT)](./jwt)
- [API-Keys](./api-keys)
Each of these strategies can work together or independently. You can also create your own custom strategies to fit your specific needs. [More details](./custom-strategies).
### HTTP-Only Cookies
[HTTP-only cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) are a highly secure method of storing identifiable data on a user's device so that Payload can automatically recognize a returning user until their cookie expires. They are totally protected from common XSS attacks and <strong>cannot be read by JavaScript in the browser</strong>, unlike JWT's. [More details](./cookies).
### JSON Web Tokens
JWT (JSON Web Tokens) can also be utilized to perform authentication. Tokens are generated on `login`, `refresh` and `me` operations and can be attached to future requests to authenticate users. [More details](./jwt).
### API Keys
API Keys can be enabled on auth collections. These are particularly useful when you want to authenticate against Payload from a third party service. [More details](./api-keys).
### Custom Strategies
There are cases where these may not be enough for your application. Payload is extendable by design so you can wire up your own strategy when you need to. [More details](./custom-strategies).
During the lifecycle of a request you will be able to access the data you have configured to be stored in the JWT by accessing `req.user`. The user object is automatically appeneded to the request for you.
### Definining Token Data
You can specify what data gets encoded to the Cookie/JWT-Token by setting `saveToJWT` property on fields within your auth collection.
```ts
import type { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
slug: 'users',
auth: true,
fields: [
{
// will be stored in the JWT
saveToJWT: true,
type: 'select',
name: 'role',
options: [
'super-admin',
'user',
]
},
{
// the entire object will be stored in the JWT
// tab fields can do the same thing!
saveToJWT: true,
type: 'group',
name: 'group1',
fields: [
{
type: 'text',
name: 'includeField',
},
{
// will be omitted from the JWT
saveToJWT: false,
type: 'text',
name: 'omitField',
},
]
},
{
type: 'group',
name: 'group2',
fields: [
{
// will be stored in the JWT
// but stored at the top level
saveToJWT: true,
type: 'text',
name: 'includeField',
},
{
type: 'text',
name: 'omitField',
},
]
},
]
}
```
<Banner type="success">
<strong>Tip:</strong>
<br/>
If you wish to use a different key other than the field `name`, you can define `saveToJWT` as a string.
</Banner>
### Using Token Data
This is especially helpful when writing [Hooks](../hooks/overview) and [Access Control](../access-control/overview) that depend on user defined fields.
Because Payload uses your existing Express server, you are free to add whatever logic you need to your app through endpoints of your own. However, Payload does not add its middleware to your Express app itself—instead, it scopes all of its middleware to Payload-specific routers.
This approach has a ton of benefits - it's great for isolation of concerns and limiting scope, but it also means that your additional routes won't have access to Payload's user authentication.
<Banner type="success">
You can make full use of Payload's built-in authentication within your own custom Express
endpoints by adding Payload's authentication middleware.
</Banner>
<Banner type="warning">
Payload must be initialized before the `payload.authenticate` middleware can be used. This is done
by calling `payload.init()` prior to adding the middleware.
</Banner>
Example in `server.js`:
```ts
import express from 'express'
import payload from 'payload'
const app = express()
const start = async () => {
await payload.init({
secret: 'PAYLOAD_SECRET_KEY',
express: app,
})
const router = express.Router()
// Note: Payload must be initialized before the `payload.authenticate` middleware can be used
Once you have created a project, you will need to select your plan. This will determine the resources that are allocated to your project and the features that are available to you.
@@ -17,7 +17,7 @@ Once you have created a project, you will need to select your plan. This will de
@@ -26,7 +26,7 @@ Once you have created a project, you will need to select your plan. This will de
| **Project Slug** | Choose a unique slug to identify your project. This needs to be unique for your team and you can change it any time. |
| **Team** | Select the team you want to create the project under. If this is your first project, a personal team will be created for you automatically. You can modify your team settings and invite new members at any time from the Team Settings page. |
### Build Settings
## Build Settings
If you are deploying a new project from a template, the following settings will be automatically configured for you. If you are using your own repository, you need to make sure your build settings are accurate for your project to deploy correctly.
@@ -39,17 +39,17 @@ If you are deploying a new project from a template, the following settings will
| **Branch to Deploy** | Select the branch of your repository that you want to deploy from. This is the branch that will be used to build your project when you commit new changes. |
| **Default Domain** | Set a default domain for your project. This must be unique and you will not able to change it. You can always add a custom domain later in your project settings. |
### Environment Variables
## Environment Variables
Any of the features in Payload Cloud that require environment variables will automatically be provided to your application. If your app requires any custom environment variables, you can set them here.
<Banner type="warning">
Note: For security reasons, any variables you wish to provide to the Admin panel must be prefixed
Note: For security reasons, any variables you wish to provide to the [Admin Panel](../admin/overview) must be prefixed
with`NEXT_PUBLIC_`. Learn more
[here](../configuration/environment-vars).
</Banner>
### Payment
## Payment
Payment methods can be set per project and can be updated any time. You can use team’s default payment method, or add a new one. Modify your payment methods in your Project settings / Team settings.
_A screenshot of the Overview page for a Cloud project._
### Database
## Database
Your Payload Cloud project comes with a MongoDB serverless Atlas DB instance or a Dedicated Atlas cluster, depending on your plan. To interact with your cloud database, you will be provided with a MongoDB connection string. This can be found under the **Database** tab of your project.
`mongodb+srv://your_connection_string`
### File Storage
## File Storage
Payload Cloud gives you S3 file storage backed by Cloudflare as a CDN, and this plugin extends Payload so that all of your media will be stored in S3 rather than locally.
AWS Cognito is used for authentication to your S3 bucket. The[Payload Cloud Plugin](https://github.com/payloadcms/plugin-cloud)will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.
### Build Settings
### Accessing Files Outside of Payload Cloud
If you'd like to access your files outside of Payload Cloud, you'll need to retrieve some values from your project's settings and put them into your environment variables. In Payload Cloud, navigate to the File Storage tab and copy the values using the copy button. Put these values in your .env file. Also copy the Cognito Password value separately and put into your .env file as well.
When you are done, you should have the following values in your .env file:
```env
PAYLOAD_CLOUD=true
PAYLOAD_CLOUD_ENVIRONMENT=prod
PAYLOAD_CLOUD_COGNITO_USER_POOL_CLIENT_ID=
PAYLOAD_CLOUD_COGNITO_USER_POOL_ID=
PAYLOAD_CLOUD_COGNITO_IDENTITY_POOL_ID=
PAYLOAD_CLOUD_PROJECT_ID=
PAYLOAD_CLOUD_BUCKET=
PAYLOAD_CLOUD_BUCKET_REGION=
PAYLOAD_CLOUD_COGNITO_PASSWORD=
```
The plugin will pick up these values and use them to access your files.
## Build Settings
You can update settings from your Project’s Settings tab. Changes to your build settings will trigger a redeployment of your project.
### Environment Variables
## Environment Variables
From the Environment Variables page of the Settings tab, you can add, update and delete variables for use in your project. Like build settings, these changes will trigger a redeployment of your project.
<Banner>
Note: For security reasons, any variables you wish to provide to the Admin panel must be prefixed
Note: For security reasons, any variables you wish to provide to the [Admin Panel](../admin/overview) must be prefixed with`NEXT_PUBLIC_`. [More details](../configuration/environment-vars).
</Banner>
### Custom Domains
## Custom Domains
With Payload Cloud, you can add custom domain names to your project. To do so, first go to the Domains page of the Settings tab of your project. Here you can see your default domain. To add a new domain, type in the domain name you wish to use.
@@ -64,27 +82,27 @@ export default buildConfig({
})
```
### Email
## Email
Powered by [Resend](https://resend.com), Payload Cloud comes with integrated email support out of the box. No configuration is needed, and you can use `payload.sendEmail()` to send email right from your Payload app. To learn more about sending email with Payload, checkout the [Email Configuration](https://payloadcms.com/docs/email/overview) overview.
If you are on the Pro or Enterprise plan, you can add your own custom Email domain name. From the Email page of your project’s Settings, add the domain you wish to use for email delivery. This will generate a set of DNS records. Add these records to your DNS provider and click verify to check that your records are resolving properly. Once verified, your emails will now be sent from your custom domain name.
### Developing Locally
## Developing Locally
To make changes to your project, you will need to clone the repository defined in your project settings to your local machine. In order to run your project locally, you will need configure your local environment first. Refer to your repository’s `README.md` file to see the steps needed for your specific template.
From there, you are ready to make updates to your project. When you are ready to make your changes live, commit your changes to the branch you specified in your Project settings, and your application will automatically trigger a redeploy and build from your latest commit.
### Cloud Plugin
## Cloud Plugin
Projects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload config:
Projects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload Config:
`yarn add @payloadcms/plugin-cloud`
```js
import { payloadCloud } from '@payloadcms/plugin-cloud'
import { buildConfig } from 'payload/config'
import { buildConfig } from 'payload'
export default buildConfig({
plugins: [payloadCloud()],
@@ -93,11 +111,11 @@ export default buildConfig({
```
<Banner type="warning">
**Note:** If your Payload config already has an email with transport, this will take precedence
**Note:** If your Payload Config already has an email with transport, this will take precedence
over Payload Cloud's email service.
</Banner>
##### **Optional configuration**
#### **Optional configuration**
If you wish to opt-out of any Payload cloud features, the plugin also accepts options to do so.

_A screenshot of the Team Settings page._
### Members
## Members
Each team has members that can interact with your projects. You can invite multiple people to your team and each individual can belong to more than one team. You can assign them either `owner` or `user` permissions. Owners are able to make admin-only changes, such as deleting projects, and editing billing information.
### Adding Members
## Adding Members
To add a new member to your team, visit your Team’s Settings page, and click “Invite Teammate”. You can then add their email address, and assign their role. Press “Save” to send the invitations, which will send an email to the invited team member where they can create a new account.
### Billing
## Billing
Users can update billing settings and subscriptions for any teams where they are designated as an `owner`. To make updates to the team’s payment methods, visit the Billing page under the Team Settings tab. You can add new cards, delete cards, and set a payment method as a default. The default payment method will be used in the event that another payment method fails.
### Subscriptions
## Subscriptions
From the Subscriptions page, a team owner can see all current plans for their team. From here, you can see the price of each plan, if there is an active trial, and when you will be billed next.
### Invoices
## Invoices
The Invoices page will you show you the invoices for your account, as well as the status on their payment.
desc: Structure your Collections for your needs by defining fields, adding slugs and labels, establishing access control, tying in hooks, setting timestamps and more.
Payload Collections are defined through configs of their own, and you can define as many as your application needs. Each
Collection will scaffold a new collection automatically in your database of choice, based on fields that you define.
A Collection is a group of records, called Documents, that all share a common schema. You can define as many Collections as your application needs. Each Document in a Collection is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.
It's often best practice to write your Collections in separate files and then import them into the main Payload config.
Collections are also used to achieve [Authentication](../authentication/overview) in Payload. By defining a Collection with `auth` options, that Collection receives additional operations to support user authentication.
## Options
Collections are the primary way to structure recurring data in your application, such as users, products, pages, posts, and other types of content that you might want to manage. Each Collection can have its own unique [Access Control](../access-control/overview), [Hooks](../hooks/overview), [Admin Options](#admin-options), and more.
| **`slug`** \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
| **`fields`** \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [Click here](/docs/fields/overview) for a full list of field types as well as how to configure them. |
| **`labels`** | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-options). |
| **`hooks`** | Entry points to "tie in" to Collection actions at specific points. [More](/docs/hooks/overview#collection-hooks) |
| **`access`** | Provide access control functions to define exactly who should be able to do what with Documents in this Collection. [More](/docs/access-control/overview/#collections) |
| **`auth`** | Specify options if you would like this Collection to feature authentication. For more, consult the [Authentication](/docs/authentication/config) documentation. |
| **`upload`** | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](/docs/upload/overview) documentation. |
| **`timestamps`** | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
| **`versions`** | Set to true to enable default options, or configure with object properties. [More](/docs/versions/overview#collection-config) |
| **`endpoints`** | Add custom routes to the REST API. Set to `false` to disable routes. [More](/docs/rest-api/overview#custom-endpoints) |
| **`graphQL`** | An object with `singularName` and `pluralName` strings used in schema generation. Auto-generated from slug if not defined. Set to `false` to disable GraphQL. |
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| **`defaultSort`** | Pass a top-level field to sort by default in the collection List view. Prefix the name of the field with a minus symbol ("-") to sort in descending order. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
_\* An asterisk denotes that a property is required._
#### Simple collection example
To define a Collection Config, use the `collection` property in your [Payload Config](./overview):
```ts
import { CollectionConfig } from 'payload/types'
import { buildConfig } from 'payload'
export const Orders: CollectionConfig = {
slug: 'orders',
fields: [
{
name: 'total',
type: 'number',
required: true,
},
{
name: 'placedBy',
type: 'relationship',
relationTo: 'customers',
required: true,
},
export default buildConfig({
// ...
collections: [ // highlight-line
// Your Collections go here
],
}
})
```
#### More collection config examples
<Banner type="success">
<strong>Tip:</strong>
If your Collection is only ever meant to contain a single Document, consider using a [Global](./globals) instead.
</Banner>
You can find an assortment
of [example collection configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/collections) in the Public
Demo source code on GitHub.
## Config Options
### Admin options
It's often best practice to write your Collections in separate files and then import them into the main [Payload Config](../overview).
You can customize the way that the Admin panel behaves on a collection-by-collection basis by defining the `admin`
| `group` | Text used as a label for grouping collection and global links together in the navigation. |
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this collection from navigation and admin routing. |
| `hooks` | Admin-specific hooks for this collection. [More](#admin-hooks) |
| `useAsTitle` | Specify a top-level field to use for a document title throughout the Admin panel. If no field is defined, the ID of the document is used as the title. |
| `description` | Text or React component to display below the Collection label in the List view to give editors more information. |
| `defaultColumns` | Array of field names that correspond to which columns to show by default in this collection's List view. |
| `disableDuplicate ` | Disables the "Duplicate" button while editing documents within this collection. |
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
| `enableRichTextLink` | The [Rich Text](/docs/fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| `enableRichTextRelationship` | The [Rich Text](/docs/fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| `preview` | Function to generate preview URLS within the Admin panel that can point to your app. [More](#preview). |
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
| `components` | Swap in your own React components to be used within this collection. [More](/docs/admin/components#collections) |
| `listSearchableFields` | Specify which fields should be searched in the List search view. [More](#list-searchable-fields) |
| **`pagination`** | Set pagination-specific options for this collection. [More](#pagination) |
### Preview
Collection `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend
of your app to preview data.
If the function is specified, a Preview button will automatically appear in the corresponding collection's Edit view.
Clicking the Preview button will link to the URL that is generated by the function.
**The preview function accepts two arguments:**
1. The document being edited
1. An `options` object, containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT.
**Example collection with preview function:**
Here is what a simple Collection Config might look like:
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
</Banner>
Here are a few options that you can specify options for pagination on a collection-by-collection basis:
| **`admin`** | The configuration options for the Admin Panel. [More details](../admin/collections). |
| **`access`**| Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |
| **`auth`** | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`disableDuplicate`** | When true, do not show the "Duplicate" button while editing documents within this Collection and prevent `duplicate` from all APIs. |
| **`defaultSort`** | Pass a top-level field to sort by default in the Collection List View. Prefix the name of the field with a minus symbol ("-") to sort in descending order. |
| **`dbName`** | Custom table or Collection name depending on the Database Adapter. Auto-generated from slug if not defined. |
| **`endpoints`** | Add custom routes to the REST API. Set to `false` to disable routes. [More details](../rest-api/overview#custom-endpoints). |
| **`fields`** \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [More details](../fields/overview). |
| **`graphQL`** | An object with `singularName` and `pluralName` strings used in schema generation. Auto-generated from slug if not defined. Set to `false` to disable GraphQL. |
| **`hooks`** | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |
| **`labels`** | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
| **`slug`** \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
| **`timestamps`** | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| **`upload`** | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |
| **`versions`** | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
### Access control
_\* An asterisk denotes that a property is required._
You can specify extremely granular access control (what users can do with documents in a collection) on a collection by
collection basis. To learn more, go to the [Access Control](/docs/access-control/overview) docs.
### Fields
Fields define the schema of the Documents within a Collection. To learn more, go to the [Fields](../fields/overview) documentation.
### Access Control
[Collection Access Control](../access-control/overview) determines what a user can and cannot do with any given Document within a Collection. To learn more, go to the [Access Control](../access-control/overview) documentation.
### Hooks
Hooks are a powerful way to extend collection functionality and execute your own logic, and can be defined on a
collection by collection basis. To learn more, go to the [Hooks](/docs/hooks/overview) documentation.
[Collection Hooks](../hooks/collections) allow you to tie into the lifecycle of your Documents so you can execute your own logic during specific events. To learn more, go to the [Hooks](../hooks/overview) documentation.
### Field types
### Admin Options
Collections support all field types that Payload has to offer—including simple fields like text and checkboxes all the
way to more complicated layout-building field groups like Blocks. [Click here](/docs/fields/overview) to learn more
about field types.
You can customize the way that the [Admin Panel](../admin/overview) behaves on a Collection-by-Collection basis. To learn more, go to the [Collection Admin Options](../admin/collections) documentation.
### List Searchable Fields
## TypeScript
In the List view, there is a "search" box that allows you to quickly find a document with a search. By default, it
searches on the ID field. If you have `admin.useAsTitle` defined, the list search will use that field. However, you can
define more than one field to search to make it easier on your admin editors to find the data they need.
You can import types from Payload to help make writing your Collection configs easier and type-safe. There are two main types that represent the Collection Config, `CollectionConfig` and `SanitizeCollectionConfig`.
For example, let's say you have a Posts collection with `title`, `metaDescription`, and `tags` fields - and you want all
three of those fields to be searchable in the List view. You can simply
add `admin.listSearchableFields: ['title', 'metaDescription', 'tags']` - and the admin UI will automatically search on
those three fields plus the ID field.
<Banner type="warning">
<strong>Note:</strong>
<br />
If you are adding <strong>listSearchableFields</strong>, make sure you index each of these fields
so your admin queries can remain performant.
</Banner>
### Admin Hooks
In addition to collection hooks themselves, Payload provides for admin UI-specific hooks that you can leverage.
**`beforeDuplicate`**
The `beforeDuplicate` hook is an async function that accepts an object containing the data to duplicate, as well as the
locale of the doc to duplicate. Within this hook, you can modify the data to be duplicated, which is useful in cases
where you have unique fields that need to be incremented or similar, as well as if you want to automatically modify a
document's `title`.
Example:
The `CollectionConfig` type represents a raw Collection Config in its full form, where only the bare minimum properties are marked as required. The `SanitizedCollectionConfig` type represents a Collection Config after it has been fully sanitized. Generally, this is only used internally by Payload.
```ts
import { BeforeDuplicate, CollectionConfig } from 'payload/types'
// Your auto-generated Page type
import { Page } from '../payload-types.ts'
const beforeDuplicate: BeforeDuplicate<Page> = ({ data }) => {
desc: Learn how to use Environment Variables in your Payload project
---
Environment Variables are a way to store sensitive information that your application needs to function. This could be anything from API keys to [Database](../database/overview) credentials. Payload allows you to easily use Environment Variables within your config and throughout your application.
## Next.js Applications
If you are using Next.js, no additional setup is required other than creating your `.env` file.
To use Environment Variables, add a `.env` file to the root of your project:
```plaintext
project-name/
├─ .env
├─ package.json
├─ payload.config.ts
```
Here is an example of what an `.env` file might look like:
For security and safety reasons, the [Admin Panel](../admin/overview) does **not** include Environment Variables in its _client-side_ bundle by default. But, Next.js provides a mechanism to expose Environment Variables to the client-side bundle when needed.
If you are building a [Custom Component](../admin/components) and need to access Environment Variables from the client-side, you can do so by prefixing them with `NEXT_PUBLIC_`.
<Banner type="warning">
<strong>Important:</strong>
Be careful about what variables you provide to your client-side code. Analyze every single one to make sure that you're not accidentally leaking sensitive information. Only ever include keys that are safe for the public to read in plain text.
</Banner>
For example, if you've got the following Environment Variable:
For more information, check out the [Next.js Documentation](https://nextjs.org/docs/app/building-your-application/configuring/environment-variables).
## Outside of Next.js
If you are using Payload outside of Next.js, we suggest using the [`dotenv`](https://www.npmjs.com/package/dotenv) package to handle Environment Variables from `.env` files. This will automatically load your Environment Variables into `process.env`.
To do this, import the package as high up in your application as possible:
```ts
import dotenv from 'dotenv'
dotenv.config() // highlight-line
import { buildConfig } from 'payload'
export default buildConfig({
serverURL: process.env.SERVER_URL,
// ...
})
```
<Banner type="warning">
<strong>Tip:</strong>
Be sure that `dotenv` can find your `.env` file. By default, it will look for a file named `.env` in the root of your project. If you need to specify a different file, pass the path into the config options.
Payload utilizes a few Express-specific middleware packages within its own routers. You can customize how they work by passing in configuration options to the main Payload config's `express` property.
### Custom Middleware
Payload allows you to pass in custom Express middleware to be used on all of the routes it opens. This is useful for adding logging or any other custom functionality to your endpoints.
There are 2 exposed properties. Each property is an array of middleware functions.
- `preMiddleware` - runs before any of the Payload middleware
- `postMiddleware` - runs after all of the Payload middleware
`express.json()` is used to parse JSON body content into JavaScript objects accessible on the Express `req`. Payload allows you to customize all of the `json` method's options. Common examples of customization use-cases are increasing the max allowed JSON body size which defaults to `2MB`.
**Example payload.config.js for how to increase the max JSON size allowed to be sent to Payload endpoints:**
```js
{
express: {
json: {
limit: '4mb',
}
}
}
```
You can find a list of all available options that are able to be passed to `express.json()` [here](https://expressjs.com/en/api.html).
### Compression
Payload uses the `compression` package to optimize transfer size for all of the routes it opens, and you can pass customization options through the Payload config.
To customize compression options, pass an object to the Payload config's `express` property.
**Example payload.config.js:**
```js
{
express: {
compression: {
// settings go here
}
}
}
```
Typically, the default options for this package are suitable. However, for a list of all available customization options, [click here](http://expressjs.com/en/resources/middleware/compression.html).
Global configs are in many ways similar to [Collections](/docs/configuration/collections). The big difference is that Collections will potentially contain _many_ documents, while a Global is a "one-off". Globals are perfect for things like header nav, site-wide banner alerts, app-wide localized strings, and other "global" data that your site or app might rely on.
Globals are in many ways similar to [Collections](../configuration/collections), except they correspond to only a single Document. You can define as many Globals as your application needs. Each Global Document is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.
As with Collection configs, it's often best practice to write your Globals in separate files and then import them into the main Payload config.
Globals are the primary way to structure singletons in Payload, such as a header navigation, site-wide banner alerts, or app-wide localized strings. Each Global can have its own unique [Access Control](../access-control/overview), [Hooks](../hooks/overview), [Admin Options](#admin-options), and more.
| **`slug`** \* | Unique, URL-friendly string that will act as an identifier for this Global. |
| **`fields`** \* | Array of field types that will determine the structure and functionality of the data stored within this Global. [Click here](/docs/fields/overview) for a full list of field types as well as how to configure them. |
| **`label`** | Text for the name in the Admin panel or an object with keys for each language. Auto-generated from slug if not defined. |
| **`description`** | Text or React component to display below the Global header to give editors more information. |
| **`admin`** | Admin-specific configuration. See below for [more detail](/docs/configuration/globals#admin-options). |
| **`hooks`** | Entry points to "tie in" to collection actions at specific points. [More](/docs/hooks/overview#global-hooks) |
| **`access`** | Provide access control functions to define exactly who should be able to do what with this Global. [More](/docs/access-control/overview/#globals) |
| **`versions`** | Set to true to enable default options, or configure with object properties. [More](/docs/versions/overview#globals-config) |
| **`endpoints`** | Add custom routes to the REST API. [More](/docs/rest-api/overview#custom-endpoints) |
| **`graphQL.name`** | Text used in schema generation. Auto-generated from slug if not defined. |
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
_\* An asterisk denotes that a property is required._
#### Simple Global example
To define a Global Config, use the `globals` property in your [Payload Config](./overview):
```ts
import { GlobalConfig } from 'payload/types'
import { buildConfig } from 'payload'
const Nav: GlobalConfig = {
export default buildConfig({
// ...
globals: [ // highlight-line
// Your Globals go here
],
})
```
<Banner type="success">
<strong>Tip:</strong>
If you have more than one Global that share the same structure, consider using a [Collection](../configuration/collections) instead.
</Banner>
## Config Options
It's often best practice to write your Globals in separate files and then import them into the main [Payload Config](./overview).
Here is what a simple Global Config might look like:
```ts
import { GlobalConfig } from 'payload'
export const Nav: GlobalConfig = {
slug: 'nav',
fields: [
{
@@ -53,90 +56,55 @@ const Nav: GlobalConfig = {
},
],
}
export default Nav
```
#### Global config example
<Banner type="success">
<strong>Reminder:</strong>
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
</Banner>
You can find a few [example Global configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/globals) in the Public Demo source code on GitHub.
| **`access`** | Provide Access Control functions to define exactly who should be able to do what with this Global. [More details](../access-control/globals). |
| **`admin`** | The configuration options for the Admin Panel. [More details](../admin/globals). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`dbName`** | Custom table or collection name for this Global depending on the Database Adapter. Auto-generated from slug if not defined. |
| **`description`** | Text or React component to display below the Global header to give editors more information. |
| **`endpoints`** | Add custom routes to the REST API. [More details](../rest-api/overview#custom-endpoints). |
| **`fields`** \* | Array of field types that will determine the structure and functionality of the data stored within this Global. [More details](../fields/overview). |
| **`graphQL.name`** | Text used in schema generation. Auto-generated from slug if not defined. |
| **`hooks`** | Entry point for Hooks. [More details](../hooks/overview#global-hooks). |
| **`label`** | Text for the name in the Admin Panel or an object with keys for each language. Auto-generated from slug if not defined. |
| **`slug`** \* | Unique, URL-friendly string that will act as an identifier for this Global. |
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| **`versions`** | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#globals-config). |
You can customize the way that the Admin panel behaves on a Global-by-Global basis by defining the `admin` property on a Global's config.
_\* An asterisk denotes that a property is required._
| `group` | Text used as a label for grouping collection and global links together in the navigation. |
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this global from navigation and admin routing. |
| `components` | Swap in your own React components to be used within this Global. [More](/docs/admin/components#globals) |
| `preview` | Function to generate a preview URL within the Admin panel for this global that can point to your app. [More](#preview). |
| `livePreview`| Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
### Fields
### Preview
Fields define the schema of the Global. To learn more, go to the [Fields](../fields/overview) documentation.
Global `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend of your app to preview data.
### Access Control
If the function is specified, a Preview button will automatically appear in the corresponding global's Edit view. Clicking the Preview button will link to the URL that is generated by the function.
**The preview function accepts two arguments:**
1. The document being edited
1. An `options` object, containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT.
As with Collections, you can specify extremely granular access control (what users can do with this Global) on a Global-by-Global basis. However, Globals only have `update` and `read` access control due to their nature of only having one document. To learn more, go to the [Access Control](/docs/access-control/overview) docs.
[Global Access Control](../access-control/globals) determines what a user can and cannot do with any given Global Document. To learn more, go to the [Access Control](../access-control/overview) documentation.
### Hooks
Globals also fully support a smaller subset of Hooks. To learn more, go to the [Hooks](/docs/hooks/overview) documentation.
[Global Hooks](../hooks/globals) allow you to tie into the lifecycle of your Documents so you can execute your own logic during specific events. To learn more, go to the [Hooks](../hooks/overview) documentation.
### Field types
### Admin Options
Globals support all field types that Payload has to offer—including simple fields like text and checkboxes all the way to more complicated layout-building field groups like Blocks. [Click here](/docs/fields/overview) to learn more about field types.
You can customize the way that the [Admin Panel](../admin/overview) behaves on a Global-by-Global basis. To learn more, go to the [Global Admin Options](../admin/globals) documentation.
### TypeScript
## TypeScript
You can import global types as follows:
You can import types from Payload to help make writing your Global configs easier and type-safe. There are two main types that represent the Global Config, `GlobalConfig` and `SanitizeGlobalConfig`.
The `GlobalConfig` type represents a raw Global Config in its full form, where only the bare minimum properties are marked as required. The `SanitizedGlobalConfig` type represents a Global Config after it has been fully sanitized. Generally, this is only used internally by Payload.
```ts
import { GlobalConfig } from 'payload/types'
// This is the type used for incoming global configs.
// Only the bare minimum properties are marked as required.
```
```ts
import { SanitizedGlobalConfig } from 'payload/types'
// This is the type used after an incoming global config is fully sanitized.
// Generally, this is only used internally by Payload.
import type { GlobalConfig, SanitizedGlobalConfig } from 'payload'
Not only does Payload support managing localized content, it also has internationalization support so that admin users can work in their preferred language. Payload's i18n support is built on top of [i18next](https://www.i18next.com). It comes included by default and can be extended in your config.
The [Admin Panel](../admin/overview) is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/beta/packages/translations). With I18n, editors can navigate the interface and read API error messages in their preferred language. This is similar to [Localization](./localization), but instead of managing translations for the data itself, you are managing translations for your application's interface.
While Payload's built-in features come translated, you may want to also translate parts of your project's configuration too. This is possible in places like collections and globals labels and groups, field labels, descriptions and input placeholder text. The admin UI will display all the correct translations you provide based on the user's language.
By default, Payload comes with preinstalled with English, but you can easily load other languages into your own application. Languages are automatically detected based on the request. If no language was detected, or if the user's language is not yet supported by your application, English will be chosen.
Here is an example of a simple collection supporting both English and Spanish editors:
To configure I18n, use the `i18n` key in your [Payload Config](./overview):
```ts
import { CollectionConfig } from 'payload/types'
import { buildConfig } from 'payload'
export const Articles: CollectionConfig = {
slug: 'articles',
labels: {
singular: {
en: 'Article',
es: 'Artículo',
},
plural: {
en: 'Articles',
es: 'Artículos',
},
export default buildConfig({
// ...
i18n: { // highlight-line
// ...
},
admin: {
group: { en: 'Content', es: 'Contenido' },
},
fields: [
{
name: 'title',
type: 'text',
label: {
en: 'Title',
es: 'Título',
},
admin: {
placeholder: { en: 'Enter title', es: 'Introduce el título' },
},
},
{
name: 'type',
type: 'radio',
options: [
{
value: 'news',
label: { en: 'News', es: 'Noticias' },
}, // etc...
],
},
],
}
})
```
### Admin UI
The Payload admin panel reads the language settings of a user's browser and display all text in that language, or will fall back to English if the user's language is not yet supported.
After a user logs in, they can change their language selection in the `/account` view.
<Banner>
<Banner type="success">
<strong>Note:</strong>
<br />
If there is a language that Payload does not yet support, we accept code
If there is a language that Payload does not yet support, we accept [code contributions](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md).
</Banner>
### Node Express
## Config Options
Payload's backend uses express middleware to set the language on incoming requests before they are handled. This allows backend validation to return error messages in the user's own language or system generated emails to be sent using the correct translation. You can make HTTP requests with the `accept-language` header and Payload will use that language.
Anywhere in your Payload app that you have access to the `req` object, you can access i18next's extensive internationalization features assigned to `req.i18n`. To access text translations you can use `req.t('namespace:key')`.
Read the i18next [API documentation](https://www.i18next.com/overview/api) to learn more.
### Configuration Options
In your Payload config, you can add translations and customize the settings in `i18n`. Payload will use your custom options and merge it with the default, allowing you to override the settings Payload provides.
**Example Payload config extending i18n:**
You can easily customize and override any of the i18n settings that Payload provides by default. Payload will use your custom options and merge them in with its own.
| **`fallbackLanguage`** | The language to fall back to if the user's preferred language is not supported. Default is `'en'`. |
| **`debug`** | Whether to log debug information to the console. Default is `false`. |
| **`translations`** | An object containing the translations. The keys are the language codes and the values are the translations. |
| **`supportedLanguages`** | An object containing the supported languages. The keys are the language codes and the values are the translations. |
## Adding Languages
You can easily add new languages to your Payload app by providing the translations for the new language. Payload maintains a number of built-in translations that can be imported from `@payloadcms/translations`, but you can also provide your own [Custom Translations](#custom-translations) to support any language.
To add a new language, use the `i18n.supportedLanguages` key in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload'
import { en } from '@payloadcms/translations/languages/en'
import { de } from '@payloadcms/translations/languages/de'
export default buildConfig({
// ...
// highlight-start
i18n: {
supportedLanguages: { en, de },
},
// highlight-end
})
```
<Banner type="warning">
<strong>Tip:</strong>
It's best to only support the languages that you need so that the bundled JavaScript is kept to a minimum for your project.
</Banner>
### Custom Translations
You can customize Payload's built-in translations either by extending existing languages or by adding new languages entirely. This can be done by injecting new translation strings into existing languages, or by providing an entirely new language keys altogether.
To add Custom Translations, use the `i18n.translations` key in your [Payload Config](./overview):
See the i18next [configuration options](https://www.i18next.com/overview/configuration-options) to learn more.
### Project Translations
While Payload's built-in features come fully translated, you may also want to translate parts of your own project. This is possible in places like [Collections](./collections) and [Globals](./globals), such as on their labels and groups, field labels, descriptions or input placeholder text.
To do this, provide the translations wherever applicable, keyed to the language code:
```ts
import { CollectionConfig } from 'payload'
export const Articles: CollectionConfig = {
slug: 'articles',
labels: {
singular: {
// highlight-start
en: 'Article',
es: 'Artículo',
// highlight-end
},
plural: {
// highlight-start
en: 'Articles',
es: 'Artículos',
// highlight-end
},
},
admin: {
group: {
// highlight-start
en: 'Content',
es: 'Contenido',
// highlight-end
},
},
fields: [
{
name: 'title',
type: 'text',
label: {
// highlight-start
en: 'Title',
es: 'Título',
// highlight-end
},
admin: {
placeholder: {
// highlight-start
en: 'Enter title',
es: 'Introduce el título'
// highlight-end
},
},
},
],
}
```
## Node
Payload's backend sets the language on incoming requests before they are handled. This allows backend validation to return error messages in the user's own language or system generated emails to be sent using the correct translation. You can make HTTP requests with the `accept-language` header and Payload will use that language.
Anywhere in your Payload app that you have access to the `req` object, you can access Payload's extensive internationalization features assigned to `req.i18n`. To access text translations you can use `req.t('namespace:key')`.
## TypeScript
In order to use custom translations in your project, you need to provide the types for the translations.
Here is an example of how you can define the types for the custom translations in a [Custom Component](../admin/components):
```ts
'use client'
import type { NestedKeysStripped } from '@payloadcms/translations'
import type React from 'react'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
const customTranslations = {
en: {
general: {
test: 'Custom Translation',
},
},
}
type CustomTranslationObject = typeof customTranslations.en
type CustomTranslationKeys = NestedKeysStripped<CustomTranslationObject>
export const MyComponent: React.FC = () => {
const { i18n, t } = useTranslation<CustomTranslationObject, CustomTranslationKeys>() // These generics merge your custom translations with the default client translations
return t('general:test')
}
```
Additionally, Payload exposes the `t` function in various places, for example in labels. Here is how you would type those:
```ts
import type {
DefaultTranslationKeys,
NestedKeysStripped,
TFunction,
} from '@payloadcms/translations'
import type { Field } from 'payload'
const customTranslations = {
en: {
general: {
test: 'Custom Translation',
},
},
}
type CustomTranslationObject = typeof customTranslations.en
type CustomTranslationKeys = NestedKeysStripped<CustomTranslationObject>
const field: Field = {
name: 'myField',
type: 'text',
label: (
{ t }: { t: TFunction<CustomTranslationKeys | DefaultTranslationKeys> }, // The generic passed to TFunction does not automatically merge the custom translations with the default translations. We need to merge them ourselves here
desc: Add and maintain as many locales as you need by adding Localization to your Payload config, set options for default locale, fallbacks, fields and more.
desc: Add and maintain as many locales as you need by adding Localization to your Payload Config, set options for default locale, fallbacks, fields and more.
Payload features deep field-based localization support. Maintaining as many locales as you need is easy. All localization support is opt-in by default. To do so, follow the two steps below.
Localization is one of the most important features of a modern CMS. It allows you to manage content in multiple languages, then serve it to your users based on their requested language. This is similar to [I18n](./i18n), but instead of managing translations for your application's interface, you are managing translations for the data itself.
### Enabling in the Payload config
With Localization, you can begin to serve personalized content to your users based on their specific language preferences, such as a multilingual website or multi-site application. There are no limits to the number of locales you can add to your Payload project.
Add the `localization` property to your Payload config to enable localization project-wide. You'll need to provide a list of all locales that you'd like to support as well as set a few other options.
**Example Payload config set up for localization:**
To configure Localization, use the `localization` key in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload/config'
import { buildConfig } from 'payload'
export default buildConfig({
collections: [
// collections go here
],
localization: {
locales: ['en', 'es', 'de'],
defaultLocale: 'en',
fallback: true,
// ...
localization: { // highlight-line
// ...
},
})
```
**Example Payload config set up for localization with full locales objects:**
## Config Options
Add the `localization` property to your Payload Config to enable Localization project-wide. You'll need to provide a list of all locales that you'd like to support as well as set a few other options.
To configure locales, use the `localization.locales` property in your [Payload Config](./overview):
```ts
import { buildConfig } from 'payload/config'
import { buildConfig } from 'payload'
export default buildConfig({
// ...
localization: {
locales: ['en', 'es', 'de'] // highlight-line
},
})
```
You can also define locales using [full configuration objects](#locale-object):
```ts
import { buildConfig } from 'payload'
export default buildConfig({
collections: [
@@ -47,7 +58,8 @@ export default buildConfig({
{
label: 'Arabic',
code: 'ar',
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left) when current locale is rtl
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left)
// when current locale is rtl
rtl: true,
},
],
@@ -57,57 +69,41 @@ export default buildConfig({
})
```
**Example Payload config set up for localization with full locales objects (including [internationalization](/docs/configuration/i18n) support):**
<Banner type="success">
<strong>Tip:</strong>
Localization works very well alongside [I18n](/docs/configuration/i18n).
| **`locales`** | Array of all the languages that you would like to support. [More details](#locales) |
| **`defaultLocale`** | Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale. |
| **`fallback`** | Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated. |
**Here is a brief explanation of each of the options available within the `localization` property:**
### Locales
**`locales`**
The locales array is a list of all the languages that you would like to support. This can be strings for each language code, or [full configuration objects](#locale-object) for more advanced options.
Array-based list of all locales that you would like to support. These can be strings of locale codes or objects with a `label`, a locale `code`, and the `rtl` (right-to-left) property. The locale codes do not need to be in any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter language and country codes (ISO 3166‑1) such as `en-US`, `en-UK`, `es-MX`, etc.
The locale codes do not need to be in any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter language and country codes (ISO 3166‑1) such as `en-US`, `en-UK`, `es-MX`, etc.
**`defaultLocale`**
#### Locale Object
Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale.
| **`code`** \* | Unique code to identify the language throughout the APIs for `locale` and `fallbackLocale` |
| **`label`** | A string to use for the selector when choosing a language, or an object keyed on the i18n keys for different languages in use. |
| **`rtl`** | A boolean that when true will make the admin UI display in Right-To-Left. |
| **`fallbackLocale`** | The code for this language to fallback to when properties of a document are not present. |
**`fallback`**
_\* An asterisk denotes that a property is required._
Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated.
## Field Localization
### Field by field localization
Payload Localization works on a **field** level—not a document level. In addition to configuring the base Payload Config to support Localization, you need to specify each field that you would like to localize.
Payload localization works on a **field** level—not a document level. In addition to configuring the base Payload config to support localization, you need to specify each field that you would like to localize.
**Here is an example of how to enable localization for a field:**
**Here is an example of how to enable Localization for a field:**
```js
{
@@ -123,29 +119,28 @@ With the above configuration, the `title` field will now be saved in the databas
All field types with a `name` property support the `localized` property—even the more complex field types like `array`s and `block`s.
<Banner>
<Banner type="info">
<strong>Note:</strong>
<br />
Enabling localization for field types that support nested fields will automatically create
Enabling Localization for field types that support nested fields will automatically create
localized "sets" of all fields contained within the field. For example, if you have a page layout
using a blocks field type, you have the choice of either localizing the full layout, by enabling
localization on the top-level blocks field, or only certain fields within the layout.
Localization on the top-level blocks field, or only certain fields within the layout.
</Banner>
<Banner type="warning">
<strong>Important:</strong>
<br />
When converting an existing field to or from `localized: true` the data structure in the document
will change for this field and so existing data for this field will be lost. Before changing the
localization setting on fields with existing data, you may need to consider a field migration
Localization setting on fields with existing data, you may need to consider a field migration
strategy.
</Banner>
### Retrieving localized docs
## Retrieving Localized Docs
When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be used.
When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be
used.
##### REST API
#### REST API
REST API locale functionality relies on URL query parameters.
@@ -155,7 +150,8 @@ Specify your desired locale by providing the `locale` query parameter directly i
**`?fallback-locale=`**
Specify fallback locale to be used by providing the `fallback-locale` query parameter. This can be provided as either a valid locale as provided to your base Payload config, or `'null'`, `'false'`, or `'none'` to disable falling back.
Specify fallback locale to be used by providing the `fallback-locale` query parameter. This can be provided as either a
valid locale as provided to your base Payload Config, or `'null'`, `'false'`, or `'none'` to disable falling back.
**Example:**
@@ -163,11 +159,13 @@ Specify fallback locale to be used by providing the `fallback-locale` query para
In the GraphQL API, you can specify `locale` and `fallbackLocale` args to all relevant queries and mutations.
The `locale` arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum values (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious to see how locales are auto-formatted, you can use the [GraphQL playground](/docs/graphql/overview#graphql-playground).
The `locale` arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum
values (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious
to see how locales are auto-formatted, you can use the [GraphQL playground](/docs/graphql/overview#graphql-playground).
The `fallbackLocale` arg will accept valid locales as well as `none` to disable falling back.
@@ -189,9 +187,11 @@ query {
arguments in nested related document queries.
</Banner>
##### Local API
#### Local API
You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options` argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid locale as well as `'null'`, `'false'`, `false`, and `'none'`.
You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options`
argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid
locale as well as `'null'`, `'false'`, `false`, and `'none'`.
desc: The Payload config is central to everything that Payload does, from adding custom React components, to modifying collections, controlling localization and much more.
desc: The Payload Config is central to everything that Payload does, from adding custom React components, to modifying collections, controlling localization and much more.
Payload is a _config-based_, code-first CMS and application framework. The Payload config is central to everything that Payload does. It scaffolds the data that Payload stores as well as maintains custom React components, hook logic, custom validations, and much more.
Payload is a _config-based_, code-first CMS and application framework. The Payload Config is central to everything that Payload does, allowing for deep configuration of your application through a simple and intuitive API. The Payload Config is a fully-typed JavaScript object that can be infinitely extended upon.
**Also, because the Payload source code is fully written in TypeScript, its configs are strongly typed—meaning that even if you aren't using TypeScript, your IDE (such as VSCode) may still provide helpful information like type-ahead suggestions while you write your config.**
Everything from your [Database](../database/overview) choice, to the appearance of the [Admin Panel](../admin/overview), is fully controlled through the Payload Config. From here you can define [Fields](../fields/overview), add [Localization](./localization), enable [Authentication](../authentication/overview), configure [Access Control](../access-control/overview), and so much more.
<Banner type="warning">
<strong>Important:</strong>
<br />
This file is included in the Payload admin bundle, so make sure you do not embed any sensitive
| `admin` \* | Base Payload admin configuration. Specify bundler*, custom components, control metadata, set the Admin user collection, and [more](/docs/admin/overview#admin-options). Required. |
| `editor` \* | Rich Text Editor which will be used by richText fields. Required. |
| `db` \* | Database Adapter which will be used by Payload. Read more [here](/docs/database/overview). Required. |
| `serverURL` | A string used to define the absolute URL of your app including the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port |
| `collections` | An array of all Collections that Payload will manage. To read more about how to define your collection configs, [click here](/docs/configuration/collections). |
| `globals` | An array of all Globals that Payload will manage. For more on Globals and their configs, [click here](/docs/configuration/globals). |
| `cors` | Either a whitelist array of URLS to allow CORS requests from, or a wildcard string (`'*'`) to accept incoming requests from any domain. |
| `localization` | Opt-in and control how Payload handles the translation of your content into multiple locales. [More](/docs/configuration/localization) |
| `graphQL` | Manage GraphQL-specific functionality here. Define your own queries and mutations, manage query complexity limits, and [more](/docs/graphql/overview#graphql-options). |
| `cookiePrefix` | A string that will be prefixed to all cookies that Payload sets. |
| `csrf` | A whitelist array of URLs to allow Payload cookies to be accepted from as a form of CSRF protection. [More](/docs/authentication/overview#csrf-protection) |
| `defaultDepth` | If a user does not specify `depth` while requesting a resource, this depth will be used. [More](/docs/getting-started/concepts#depth) |
| `maxDepth` | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. |
| `indexSortableFields` | Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. |
| `upload` | Base Payload upload configuration. [More](/docs/upload/overview#payload-wide-upload-options). |
| `routes` | Control the routing structure that Payload binds itself to. Specify `admin`, `api`, `graphQL`, and `graphQLPlayground`. |
| `email` | Base email settings to allow Payload to generate email such as Forgot Password requests and other requirements. [More](/docs/email/overview#configuration) |
| `express` | Express-specific middleware options such as compression and JSON parsing. [More](/docs/configuration/express) |
| `debug` | Enable to expose more detailed error information. |
| `rateLimit` | Control IP-based rate limiting for all Payload resources. Used to prevent DDoS attacks and [more](/docs/production/preventing-abuse#rate-limiting-requests). |
| `hooks` | Tap into Payload-wide hooks. [More](/docs/hooks/overview) |
| `plugins` | An array of Payload plugins. [More](/docs/plugins/overview) |
| `endpoints` | An array of custom API endpoints added to the Payload router. [More](/docs/rest-api/overview#custom-endpoints) |
| `custom` | Extension point for adding custom data (e.g. for plugins) |
_\* An asterisk denotes that a property is required._
#### Simple example
The Payload Config is a `payload.config.ts` file typically located in the root of your project:
```ts
import { buildConfig } from 'payload/config'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { postgresAdapter } from '@payloadcms/db-postgres' // beta
import { viteBundler } from '@payloadcms/bundler-vite'
import { webpackBundler } from '@payloadcms/bundler-webpack'
import { lexicalEditor } from '@payloadcms/richtext-lexical' // beta
import { slateEditor } from '@payloadcms/richtext-slate'
import { buildConfig } from 'payload'
export default buildConfig({
admin: {
bundler: webpackBundler(), // or viteBundler()
},
db: mongooseAdapter({}) // or postgresAdapter({}),
editor: lexicalEditor({}) // or slateEditor({})
// Your config goes here
})
```
The Payload Config is strongly typed and ties directly into Payload's TypeScript codebase. This means your IDE (such as VSCode) will provide helpful information like type-ahead suggestions while you write your config.
<Banner type="success">
<strong>Tip:</strong>
The location of your Payload Config can be customized. [More details](#customizing--automating-config-location-detection).
</Banner>
## Config Options
To author your Payload Config, first determine which [Database](../database/overview) you'd like to use, then use [Collections](./collections) or [Globals](./globals) to define the schema of your data.
Here is one of the simplest possible Payload configs:
```ts
import { buildConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
export default buildConfig({
secret: process.env.PAYLOAD_SECRET,
db: mongooseAdapter({
url: process.env.DATABASE_URI,
}),
collections: [
{
slug: 'pages',
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'content',
type: 'richText',
required: true,
},
],
},
],
globals: [
{
slug: 'header',
fields: [
{
name: 'nav',
type: 'array',
fields: [
{
name: 'page',
type: 'relationship',
relationTo: 'pages',
},
],
},
],
},
type: 'text'
}
]
}
],
})
```
#### Full example config
<Banner type="success">
<strong>Note:</strong>
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
</Banner>
You can see a full [example config](https://github.com/payloadcms/public-demo/blob/master/src/payload/payload.config.ts) in the Public Demo source code on GitHub.
| **`admin`** | The configuration options for the Admin Panel, including Custom Components, Live Preview, etc. [More details](../admin/overview#admin-options). |
| **`bin`** | Register custom bin scripts for Payload to execute. |
| **`editor`** | The Rich Text Editor which will be used by `richText` fields. [More details](../rich-text/overview). |
| **`db`** \* | The Database Adapter which will be used by Payload. [More details](../database/overview). |
| **`serverURL`** | A string used to define the absolute URL of your app. This includes the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port. |
| **`collections`** | An array of Collections for Payload to manage. [More details](./collections). |
| **`compatibility`** | Compatibility flags for earlier versions of Payload. [More details](#compatibility-flags). |
| **`globals`** | An array of Globals for Payload to manage. [More details](./globals). |
| **`cors`** | Cross-origin resource sharing (CORS) is a mechanism that accept incoming requests from given domains. You can also customize the `Access-Control-Allow-Headers` header. [More details](#cors). |
| **`localization`** | Opt-in to translate your content into multiple locales. [More details](./localization). |
| **`logger`** | Logger options, logger options with a destination stream, or an instantiated logger instance. [More details](https://getpino.io/#/docs/api?id=options). |
| **`graphQL`** | Manage GraphQL-specific functionality, including custom queries and mutations, query complexity limits, etc. [More details](../graphql/overview#graphql-options). |
| **`cookiePrefix`** | A string that will be prefixed to all cookies that Payload sets. |
| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/overview#csrf-protection). |
| **`defaultDepth`** | If a user does not specify `depth` while requesting a resource, this depth will be used. [More details](../queries/depth). |
| **`defaultMaxTextLength`** | The maximum allowed string length to be permitted application-wide. Helps to prevent malicious public document creation. |
| **`maxDepth`** | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. [More details](../queries/depth). |
| **`indexSortableFields`** | Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. |
| **`upload`** | Base Payload upload configuration. [More details](../upload/overview#payload-wide-upload-options). |
| **`routes`** | Control the routing structure that Payload binds itself to. [More details](../admin/overview#root-level-routes). |
| **`email`** | Configure the Email Adapter for Payload to use. [More details](../email/overview). |
| **`debug`** | Enable to expose more detailed error information. |
| **`rateLimit`** | Control IP-based rate limiting for all Payload resources. Used to prevent DDoS attacks, etc. [More details](../production/preventing-abuse#rate-limiting-requests). |
| **`hooks`** | An array of Root Hooks. [More details](../hooks/overview). |
| **`plugins`** | An array of Plugins. [More details](../plugins/overview). |
| **`endpoints`** | An array of Custom Endpoints added to the Payload router. [More details](../rest-api/overview#custom-endpoints). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
| **`i18n`** | Internationalization configuration. Pass all i18n languages you'd like the admin UI to support. Defaults to English-only. [More details](./i18n). |
| **`secret`** \* | A secure, unguessable string that Payload will use for any encryption workflows - for example, password salt / hashing. |
| **`sharp`** | If you would like Payload to offer cropping, focal point selection, and automatic media resizing, install and pass the Sharp module to the config here. |
We suggest using the `dotenv` package to handle environment variables alongside of Payload. All that's necessary to do is to require the package as high up in your application as possible (for example, at the top of your `server.js` file), and ensure that it can find an `.env` file that you create.
_\* An asterisk denotes that a property is required._
**Add this line to the top of your server:**
<Banner type="warning">
<strong>Note:</strong>
Some properties are removed from the client-side bundle. [More details](../admin/components#accessing-the-payload-config).
</Banner>
```
require('dotenv').config()
// ...
// the rest of your `server.js` file goes here
### Typescript Config
Payload exposes a variety of TypeScript settings that you can leverage. These settings are used to auto-generate TypeScript interfaces for your [Collections](../configuration/collections) and [Globals](../configuration/globals), and to ensure that Payload uses your [Generated Types](../typescript/overview) for all [Local API](../local-api/overview) methods.
To customize the TypeScript settings, use the `typescript` property in your Payload Config:
```ts
import { buildConfig } from 'payload'
export default buildConfig({
// ...
typescript: { // highlight-line
// ...
}
})
```
Note that if you rely on any environment variables in your config itself, you should also call `dotenv()` at the top of your config itself as well. There's no harm in calling it in both your server and your config itself!
The following options are available:
**Here is an example project structure w/ `dotenv` and an `.env` file:**
| Option | Description |
| --------------- | --------------------- |
| **`autoGenerate`** | By default, Payload will auto-generate TypeScript interfaces for all collections and globals that your config defines. Opt out by setting `typescript.autoGenerate: false`. [More details](../typescript/overview). |
| **`declare`** | By default, Payload adds a `declare` block to your generated types, which makes sure that Payload uses your generated types for all Local API methods. Opt out by setting `typescript.declare: false`. |
| **`outputFile`** | Control the output path and filename of Payload's auto-generated types by defining the `typescript.outputFile` property to a full, absolute path. |
```
project-name
---- .env
---- package.json
---- payload.config.js
---- server.js
```
## Config Location
For Payload command-line scripts, we need to be able to locate your Payload Config. We'll check a variety of locations for the presence of `payload.config.ts` by default, including:
1. The root current working directory
1. The `compilerOptions` in your `tsconfig`*
1. The `dist` directory*
_\* Config location detection is different between development and production environments. See below for more details._
<Banner type="warning">
<strong>Important:</strong>
<br />
If you use an environment variable to configure any properties that are required for the Admin
panel to function (ex. serverURL or any routes), you need to make sure that your Admin panel code
can access it. [Click here](/docs/admin/webpack#admin-environment-vars) for more info.
Ensure your `tsconfig.json` is properly configured for Payload to auto-detect your config location. If if does not exist, or does not specify the proper `compilerOptions`, Payload will default to the current working directory.
Payload is designed to automatically locate your configuration file. By default, it will first look in the root of your current working directory for a file named `payload.config.js` or `payload.config.ts` if you're using TypeScript.
In development mode, if the configuration file is not found at the root, Payload will attempt to read your `tsconfig.json`, and attempt to find the config file specified in the `rootDir`:
In development mode, if the configuration file is not found at the root, Payload will attempt to read your `tsconfig.json`, and search in the directory specified in `compilerOptions.rootDir` (typically "src").
```json
{
// ...
// highlight-start
"compilerOptions": {
"rootDir": "src"
}
// highlight-end
}
```
In production mode, Payload will first attempt to find the config file in the output directory specified in `compilerOptions.outDir` of your `tsconfig.json`, then fallback to the source directory (`compilerOptions.rootDir`), and finally will check the 'dist' directory.
**Production Mode**
Please ensure your `tsconfig.json` is properly configured if you want Payload to accurately auto-detect your configuration file location. If `tsconfig.json` does not exist or doesn't specify `rootDir` or `outDir`, Payload will default to the current working directory.
In production mode, Payload will first attempt to find the config file in the `outDir` of your `tsconfig.json`, and if not found, will fallback to the `rootDor` directory:
#### Overriding the Config Location
```json
{
// ...
// highlight-start
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
}
// highlight-end
}
```
In addition to the above automated detection, you can specify your own location for the Payload config file. This is done by using the environment variable `PAYLOAD_CONFIG_PATH`. The path you provide via this environment variable can either be absolute or relative to your current working directory. This can be useful in situations where your Payload config is not in a standard location, or you wish to switch between multiple configurations.
If none was in either location, Payload will finally check the `dist` directory.
**Example in package.json:**
### Customizing the Config Location
In addition to the above automated detection, you can specify your own location for the Payload Config. This can be useful in situations where your config is not in a standard location, or you wish to switch between multiple configurations. To do this, Payload exposes an [Environment Variable](..environment-variables) to bypass all automatic config detection.
To use a custom config location, set the `PAYLOAD_CONFIG_PATH` environment variable:
When `PAYLOAD_CONFIG_PATH` is set, Payload will use this path to load the configuration, bypassing all automated detection.
<Banner type="info">
<strong>Tip:</strong>
`PAYLOAD_CONFIG_PATH` can be either an absolute path, or path relative to your current working directory.
</Banner>
### Developing within the Config
## Telemetry
Payload comes with `isomorphic-fetch` installed which means that even in Node, you can use the `fetch` API just as you would within the browser. No need to import `axios` or similar, unless you want to!
### TypeScript
You can import config types as follows:
```ts
import { Config } from 'payload/config'
// This is the type used for an incoming Payload config.
// Only the bare minimum properties are marked as required.
```
```ts
import { SanitizedConfig } from 'payload/config'
// This is the type used after an incoming Payload config is fully sanitized.
// Generally, this is only used internally by Payload.
```
### Telemetry
Payload collects **completely anonymous** telemetry data about general usage. This data is super important to us and helps us accurately understand how we're growing and what we can do to build the software into everything that it can possibly be. The telemetry that we collect also help us demonstrate our growth in an accurate manner, which helps us as we seek investment to build and scale our team. If we can accurately demonstrate our growth, we can more effectively continue to support Payload as free and open-source software. To opt out of telemetry, you can pass `telemetry: false` within your Payload config.
Payload collects **completely anonymous** telemetry data about general usage. This data is super important to us and helps us accurately understand how we're growing and what we can do to build the software into everything that it can possibly be. The telemetry that we collect also help us demonstrate our growth in an accurate manner, which helps us as we seek investment to build and scale our team. If we can accurately demonstrate our growth, we can more effectively continue to support Payload as free and open-source software. To opt out of telemetry, you can pass `telemetry: false` within your Payload Config.
For more information about what we track, take a look at our [privacy policy](/privacy).
## Cross-origin resource sharing (CORS)
Cross-origin resource sharing (CORS) can be configured with either a whitelist array of URLS to allow CORS requests from, a wildcard string (`*`) to accept incoming requests from any domain, or a object with the following properties:
| **`origins`** | Either a whitelist array of URLS to allow CORS requests from, or a wildcard string (`'*'`) to accept incoming requests from any domain. |
| **`headers`** | A list of allowed headers that will be appended in `Access-Control-Allow-Headers`. |
Here's an example showing how to allow incoming requests from any domain:
```ts
import { buildConfig } from 'payload/config'
export default buildConfig({
// ...
cors: '*' // highlight-line
})
```
Here's an example showing how to append a new header (`x-custom-header`) in `Access-Control-Allow-Headers`:
```ts
import { buildConfig } from 'payload/config'
export default buildConfig({
// ...
// highlight-start
cors: {
origins: ['http://localhost:3000']
headers: ['x-custom-header']
}
// highlight-end
})
```
## TypeScript
You can import types from Payload to help make writing your config easier and type-safe. There are two main types that represent the Payload Config, `Config` and `SanitizedConfig`.
The `Config` type represents a raw Payload Config in its full form. Only the bare minimum properties are marked as required. The `SanitizedConfig` type represents a Payload Config after it has been fully sanitized. Generally, this is only used internally by Payload.
```ts
import type { Config, SanitizedConfig } from 'payload'
```
## Server vs. Client
The Payload Config only lives on the server and is not allowed to contain any client-side code. That way, you can load up the Payload Config in any server environment or standalone script, without having to use Bundlers or Node.js loaders to handle importing client-only modules (e.g. scss files or React Components) without any errors.
Behind the curtains, the Next.js-based Admin Panel generates a ClientConfig, which strips away any server-only code and enriches the config with React Components.
## Compatibility flags
The Payload Config can accept compatibility flags for running the newest versions but with older databases. You should only use these flags if you need to, and should confirm that you need to prior to enabling these flags.
`allowLocalizedWithinLocalized`
Payload localization works on a field-by-field basis. As you can nest fields within other fields, you could potentially nest a localized field within a localized field—but this would be redundant and unnecessary. There would be no reason to define a localized field within a localized parent field, given that the entire data structure from the parent field onward would be localized.
By default, Payload will remove the `localized: true` property from sub-fields if a parent field is localized. Set this compatibility flag to `true` only if you have an existing Payload MongoDB database from pre-3.0, and you have nested localized fields that you would like to maintain without migrating.
desc: Payload features first-party database migrations all done in TypeScript.
---
Payload exposes a full suite of migration controls available for your use. Migration commands are accessible via the `npm run payload` command in your project directory.
Payload exposes a full suite of migration controls available for your use. Migration commands are accessible via
the `npm run payload` command in your project directory.
Ensure you have an npm script called "payload" in your `package.json` file.
@@ -19,38 +20,48 @@ Ensure you have an npm script called "payload" in your `package.json` file.
```
<Banner>
Note that you need to run Payload migrations through the package manager that you are using, because Payload should not be globally installed on your system.
Note that you need to run Payload migrations through the package manager that you are using,
because Payload should not be globally installed on your system.
</Banner>
### Migration file contents
## Migration file contents
Payload stores all created migrations in a folder that you can specify. By default, migrations are stored in `./src/migrations`.
Payload stores all created migrations in a folder that you can specify. By default, migrations are stored
in `./src/migrations`.
A migration file has two exports - an `up` function, which is called when a migration is executed, and a `down` function that will be called if for some reason the migration fails to complete successfully. The `up` function should contain all changes that you attempt to make within the migration, and the `down` should ideally revert any changes you make.
A migration file has two exports - an `up` function, which is called when a migration is executed, and a `down` function
that will be called if for some reason the migration fails to complete successfully. The `up` function should contain
all changes that you attempt to make within the migration, and the `down` should ideally revert any changes you make.
For an added level of safety, migrations should leverage Payload [transactions](/docs/database/transactions).
For an added level of safety, migrations should leverage Payload [transactions](/docs/database/transactions). Migration
functions should make use of the `req` by adding it to the arguments of your Payload Local API calls such
as `payload.create` and Database Adapter methods like `payload.db.create`.
Here is an example migration file:
```ts
import { MigrateUpArgs, MigrateDownArgs } from '@payloadcms/your-db-adapter'
export async function up({ payload }: MigrateUpArgs): Promise<void> {
export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
// Perform changes to your database here.
// You have access to `payload` as an argument, and
// everything is done in TypeScript.
};
}
export async function down({ payload }: MigrateDownArgs): Promise<void> {
export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
// Do whatever you need to revert changes if the `up` function fails
};
}
```
### Migrations Directory
## Migrations Directory
Each DB adapter has an optional property `migrationDir` where you can override where you want your migrations to be stored/read. If this is not specified, Payload will check the default and possibly make a best effort to find your migrations directory by searching in common locations ie. `./src/migrations`, `./dist/migrations`, `./migrations`, etc.
Each DB adapter has an optional property `migrationDir` where you can override where you want your migrations to be
stored/read. If this is not specified, Payload will check the default and possibly make a best effort to find your
migrations directory by searching in common locations ie. `./src/migrations`, `./dist/migrations`, `./migrations`, etc.
All database adapters should implement similar migration patterns, but there will be small differences based on the adapter and its specific needs. Below is a list of all migration commands that should be supported by your database adapter.
All database adapters should implement similar migration patterns, but there will be small differences based on the
adapter and its specific needs. Below is a list of all migration commands that should be supported by your database
adapter.
## Commands
@@ -64,7 +75,8 @@ npm run payload migrate
### Create
Create a new migration file in the migrations directory. You can optionally name the migration that will be created. By default, migrations will be named using a timestamp.
Create a new migration file in the migrations directory. You can optionally name the migration that will be created. By
default, migrations will be named using a timestamp.
```text
npm run payload migrate:create optional-name-here
@@ -72,7 +84,8 @@ npm run payload migrate:create optional-name-here
### Status
The `migrate:status` command will check the status of migrations and output a table of which migrations have been run, and which migrations have not yet run.
The `migrate:status` command will check the status of migrations and output a table of which migrations have been run,
and which migrations have not yet run.
`payload migrate:status`
@@ -111,3 +124,119 @@ Drops all entities from the database and re-runs all migrations from scratch.
```text
npm run payload migrate:fresh
```
## When to run migrations
Depending on which Database Adapter you use, your migration workflow might differ subtly.
In relational databases, migrations will be **required** for non-development database environments. But with MongoDB, you might only need to run migrations once in a while (or never even need them).
#### MongoDB
In MongoDB, you'll only ever really need to run migrations for times where you change your database shape, and you have lots of existing data that you'd like to transform from Shape A to Shape B.
In this case, you can create a migration by running `pnpm payload migrate:create`, and then write the logic that you need to perform to migrate your documents to their new shape. You can then either run your migrations in CI before you build / deploy, or you can run them locally, against your production database, by using your production database connection string on your local computer and running the `pnpm payload migrate` command.
#### Postgres
In relational databases like Postgres, migrations are a bit more important, because each time you add a new field or a new collection, you'll need to update the shape of your database to match your Payload Config (otherwise you'll see errors upon trying to read / write your data).
That means that Postgres users of Payload should become familiar with the entire migration workflow from top to bottom.
Here is an overview of a common workflow for working locally against a development database, creating migrations, and then running migrations against your production database before deploying.
**1 - work locally using push mode**
Payload uses Drizzle ORM's powerful `push` mode to automatically sync data changes to your database for you while in development mode. By default, this is enabled and is the suggested workflow to using Postgres and Payload while doing local development.
You can disable this setting and solely use migrations to manage your local development database (pass `push: false` to your Postgres adapter), but if you do disable it, you may see frequent errors while running development mode. This is because Payload will have updated to your new data shape, but your local database will not have updated.
For this reason, we suggest that you leave `push` as its default setting and treat your local dev database as a sandbox.
For more information about push mode and prototyping in development, [click here](/docs/beta/database/postgres#prototyping-in-dev-mode).
The typical workflow in Payload is to build out your Payload configs, install plugins, and make progress in development mode - allowing Drizzle to push your changes to your local database for you. Once you're finished, you can create a migration.
But importantly, you do not need to run migrations against your development database, because Drizzle will have already pushed your changes to your database for you.
<Banner type="warning">
Warning: do not mix "push" and migrations with your local development database. If you use "push"
locally, and then try to migrate, Payload will throw a warning, telling you that these two methods
are not meant to be used interchangeably.
</Banner>
**2 - create a migration**
Once you're done with working in your Payload Config, you can create a migration. It's best practice to try and complete a specific task or fully build out a feature before you create a migration.
But once you're ready, you can run `pnpm payload migrate:create`, which will perform the following steps for you:
- We will look for any existing migrations, and automatically generate SQL changes necessary to convert your schema from its prior state to the new state of your Payload Config
- We will then create a new migration file in your `/migrations` folder that contains all the SQL necessary to be run
We won't immediately run this migration for you, however.
<Banner type="success">
Tip: migrations created by Payload are relatively programmatic in nature, so there should not be any surprises, but before you check in the created migration it's a good idea to always double-check the contents of the migration files.
</Banner>
**3 - set up your build process to run migrations**
Generally, you want to run migrations before you build Payload for production. This typically happens in your CI pipeline and can usually be configured on platforms like Payload Cloud, Vercel, or Netlify by specifying your build script.
A common set of scripts in a `package.json`, set up to run migrations in CI, might look like this:
```js
"scripts": {
// For running in dev mode
"dev": "next dev --turbo",
// To build your Next + Payload app for production
"build": "next build",
// A "tie-in" to Payload's CLI for convenience
// this helps you run `pnpm payload migrate:create` and similar
// This command is what you'd set your `build script` to.
// Notice how it runs `payload migrate` and then `pnpm build`?
// This will run all migrations for you before building, in your CI,
// against your production database
"ci": "payload migrate && pnpm build",
},
```
In the example above, we've specified a `ci` script which we can use as our "build script" in the platform that we are deploying to production with.
This will require that your build pipeline can connect to your database, and it will simply run the `payload migrate` command prior to starting the build process. By calling `payload migrate`, Payload will automatically execute any migrations in your `/migrations` folder that have not yet been executed against your production database, in the order that they were created.
If it fails, the deployment will be rejected. But now, with your build script set up to run your migrations, you will be all set! Next time you deploy, your CI will execute the required migrations for you, and your database will be caught up with the shape that your Payload Config requires.
## Running migrations in production
In certain cases, you might want to run migrations at runtime when the server starts. Running them during build time may be impossible due to not having access to your database connection while building or similar reasoning.
If you're using a long-running server or container where your Node server starts up one time and then stays initialized, you might prefer to run migrations on server startup instead of within your CI.
In order to run migrations at runtime, on initialization, you can pass your migrations to your database adapter under the `prodMigrations` key as follows:
```ts
// Import your migrations from the `index.ts` file
// that Payload generates for you
import { migrations } from './migrations'
import { buildConfig } from 'payload'
export default buildConfig({
// your config here
db: postgresAdapter({
// your adapter config here
prodMigrations: migrations
})
})
```
Passing your migrations as shown above will tell Payload, in production only, to execute any migrations that need to be run prior to completing the initialization of Payload. This is ideal for long-running services where Payload will only be initialized at startup.
<Banner type="warning">
Warning - if Payload is instructed to run migrations in production, this may slow down serverless cold starts on platforms such as Vercel. Generally, this option should only be used for long-running servers / containers.
To use Payload with MongoDB, install the package `@payloadcms/db-mongodb`. It will come with everything you need to store your Payload data in MongoDB.
To use Payload with MongoDB, install the package `@payloadcms/db-mongodb`. It will come with everything you need to
store your Payload data in MongoDB.
Then from there, pass it to your Payload config as follows:
Then from there, pass it to your Payload Config as follows:
```ts
import { mongooseAdapter } from '@payloadcms/db-mongodb'
| `autoPluralization` | Tell Mongoose to auto-pluralize any collection names if it encounters any singular words used as collection `slug`s. |
| `connectOptions` | Customize MongoDB connection options. Payload will connect to your MongoDB database using default options which you can override and extend to include all the [options](https://mongoosejs.com/docs/connections.html#options) available to mongoose. |
| `disableIndexHints` | Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination, as it increases the speed of the count function used in that query. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false |
| `migrationDir` | Customize the directory that migrations are stored. |
| Option | Description |
| -------------------- | ----------- |
| `autoPluralization` | Tell Mongoose to auto-pluralize any collection names if it encounters any singular words used as collection `slug`s. |
| `connectOptions` | Customize MongoDB connection options. Payload will connect to your MongoDB database using default options which you can override and extend to include all the [options](https://mongoosejs.com/docs/connections.html#options) available to mongoose. |
| `disableIndexHints` | Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination, as it increases the speed of the count function used in that query. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false |
| `migrationDir` | Customize the directory that migrations are stored. |
| `transactionOptions` | An object with configuration properties used in [transactions](https://www.mongodb.com/docs/manual/core/transactions/) or `false` which will disable the use of transactions. |
| `collation` | Enable language-specific string comparison with customizable options. Available on MongoDB 3.4+. Defaults locale to "en". Example: `{ strength: 3 }`. For a full list of collation options and their definitions, see the [MongoDB documentation](https://www.mongodb.com/docs/manual/reference/collation/). |
### Access to Mongoose models
## Access to Mongoose models
After Payload is initialized, this adapter exposes all of your Mongoose models and they are available for you to work with directly.
After Payload is initialized, this adapter exposes all of your Mongoose models and they are available for you to work
desc: With Payload, you bring your own database and own your data. You have full control.
---
Payload interacts with your database via the database adapter that you choose. Right now, Payload officially supports two database adapters:
Payload is database agnostic, meaning you can use any type of database behind Payload's familiar APIs. Payload is designed to interact with your database through a Database Adapter, which is a thin layer that translates Payload's internal data structures into your database's native data structures.
Currently, Payload officially supports the following Database Adapters:
We will be adding support for SQLite and MySQL in the near future using Drizzle ORM.
- [MongoDB](/docs/database/mongodb) with [Mongoose](https://mongoosejs.com/)
- [Postgres](/docs/database/postgres) with [Drizzle](https://drizzle.team/)
- [SQLite](/docs/database/sqlite) with [Drizzle](https://drizzle.team/)
To use a specific database adapter, you need to install it and configure it according to its own specifications. Visit the documentation for your applicable database adapter to learn more.
To configure a Database Adapter, use the `db` property in your [Payload Config](../configuration/overview):
## Selecting a database
```ts
import { buildConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
export default buildConfig({
// ...
// highlight-start
db: mongooseAdapter({
url: process.env.DATABASE_URI,
}),
// highlight-end
})
```
<Banner type="warning">
<strong>Reminder:</strong>
The Database Adapter is an external dependency and must be installed in your project separately from Payload. You can find the installation instructions for each Database Adapter in their respective documentation.
</Banner>
## Selecting a Database
There are several factors to consider when choosing which database technology and hosting option is right for your project and workload. Payload can theoretically support any database, but it's up to you to decide which database to use.
#### When to use MongoDB
There are two main categories of databases to choose from:
If your project has a lot of dynamic fields, and you are comfortable with allowing Payload to enforce data integrity across your documents, MongoDB is a great choice. With it, your Payload documents are stored as _one_ document in your database—no matter if you have localization enabled, how many block or array fields you have, etc. This means that the shape of your data in your database will very closely reflect your field schema, and there is minimal complexity involved in storing or retrieving your data.
@@ -27,47 +52,21 @@ You should prefer MongoDB if:
- You prefer simplicity within your database
- You don't want to deal with keeping production / staging databases in sync via [DDL changes](https://en.wikipedia.org/wiki/Data_definition_language)
- Most (or everything) in your project is localized
- You leverage a lot of array fields, block fields, or `hasMany` selectfields and similar
- Most (or everything) in your project is [Localized](../configuration/localization)
- You leverage a lot of [Arrays](../fields/array), [Blocks](../fields/blocks), or `hasMany` [Select](../fields/select) fields
#### When to use a relational DB
### Relational Databases
Many projects might call for more rigid database architecture where the shape of your data is strongly enforced at the database level. For example, if you know the shape of your data and it's relatively "flat", and you don't anticipate it to change often, your workload might suit relational databases like Postgres very well.
You should prefer a relational DB like Postgres if:
You should prefer a relational DB like Postgres or SQLite if:
- You are comfortable with migration workflows
- You are comfortable with [Migrations](./migrations)
- You require enforced data consistency at the database level
- You have a lot of relationships between collections and require relationships to be enforced
#### Differences in Payload features
## Payload Differences
It's important to note that almost everything Payload does is available in all of our officially supported database adapters, including localization, arrays, blocks, etc.
It's important to note that nearly every Payload feature is available in all of our officially supported Database Adapters, including [Localization](../configuration/localization), [Arrays](../fields/array), [Blocks](../fields/blocks), etc. The only thing that is not supported in Postgres yet is the [Point Field](/docs/fields/point), but that should be added soon.
The only thing that is not supported in Postgres yet is the [Point field](/docs/fields/point), but that should be added soon.
It's up to you to choose which database you would like to use.
## Configuration
To configure the database for your Payload application, an adapter can be assigned to `config.db`. This property is required within your Payload config.
Here's an example:
```ts
import { postgresAdapter } from '@payloadcms/db-postgres'
export default buildConfig({
// Your config goes here
collections: [
// Collections go here
],
// Here is where you pass your database adapter
// and the adapter will require options specific to itself
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI,
}
}),
})
```
It's up to you to choose which database you would like to use based on the requirements of your project. Payload has no opinion on which database you should ultimately choose.
To use Payload with Postgres, install the package `@payloadcms/db-postgres`. It leverages Drizzle ORM and `node-postgres` to interact with a Postgres database that you provide.
<Banner>
The Postgres database adapter is currently in beta. If you would like to help us test this package, we'd love to hear if you find any bugs or issues!
</Banner>
Alternatively, the `@payloadcms/db-vercel-postgres` package is also available and is optimized for use with Vercel.
It automatically manages changes to your database for you in development mode, and exposes a full suite of migration controls for you to leverage in order to keep other database environments in sync with your schema. DDL transformations are automatically generated.
To configure Payload to use Postgres, pass the `postgresAdapter` to your Payload config as follows:
To configure Payload to use Postgres, pass the `postgresAdapter` to your Payload Config as follows:
### Usage
`@payloadcms/db-postgres`:
```ts
import { postgresAdapter } from '@payloadcms/db-postgres'
| `pool` | [Pool connection options](https://orm.drizzle.team/docs/quick-postgresql/node-postgres) that will be passed to Drizzle and `node-postgres`. |
| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |
| `migrationDir` | Customize the directory that migrations are stored. |
```ts
import { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'
### Access to Drizzle
export default buildConfig({
// Automatically uses proces.env.POSTGRES_URL if no options are provided.
db: vercelPostgresAdapter(),
// Optionally, can accept the same options as the @vercel/postgres package.
| `pool` \* | [Pool connection options](https://orm.drizzle.team/docs/quick-postgresql/node-postgres) that will be passed to Drizzle and `node-postgres` or to `@vercel/postgres` |
| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |
| `migrationDir` | Customize the directory that migrations are stored. |
| `schemaName` (experimental) | A string for the postgres schema to use, defaults to 'public'. |
| `idType` | A string of 'serial', or 'uuid' that is used for the data type given to id columns. |
| `transactionOptions` | A PgTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) |
| `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '_locales'. |
| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '_rels'. |
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |
## Access to Drizzle
After Payload is initialized, this adapter will expose the full power of Drizzle to you for use if you need it.
@@ -53,7 +74,7 @@ You can access Drizzle as follows:
payload.db.drizzle
```
### Tables, relations, and enums
## Tables, relations, and enums
In addition to exposing Drizzle directly, all of the tables, Drizzle relations, and enum configs are exposed for you via the `payload.db` property as well.
@@ -61,27 +82,18 @@ In addition to exposing Drizzle directly, all of the tables, Drizzle relations,
- Enums - `payload.db.enums`
- Relations - `payload.db.relations`
### Prototyping in development mode
## Prototyping in development mode
Drizzle exposes two ways to work locally in development mode.
The first is [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push), which automatically pushes changes you make to your Payload config (and therefore, Drizzle schema) to your database so you don't have to manually migrate every time you change your Payload config. This only works in development mode, and should not be mixed with manually running [`migrate`](/docs/database/migrations) commands.
The first is [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push), which automatically pushes changes you make to your Payload Config (and therefore, Drizzle schema) to your database so you don't have to manually migrate every time you change your Payload Config. This only works in development mode, and should not be mixed with manually running [`migrate`](/docs/database/migrations) commands.
You will be warned if any changes that you make will entail data loss while in development mode. Push is enabled by default, but you can opt out if you'd like.
Alternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload config.
Alternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload Config.
### Migration workflows
## Migration workflows
Migrations are extremely powerful thanks to the seamless way that Payload and Drizzle work together. Let's take the following scenario:
In Postgres, migrations are a fundamental aspect of working with Payload and you should become familiar with how they work.
1. You are building your Payload config locally, with a local database used for testing.
1. You have left the default setting of `push` enabled, so every time you change your Payload config (add or remove fields, collections, etc.), Drizzle will automatically push changes to your local DB.
1. Once you're done with your changes, or have completed a feature, you can run `npm run payload migrate:create`.
1. Payload and Drizzle will look for any existing migrations, and automatically generate all SQL changes necessary to convert your schema from its prior state into the state of your current Payload config, and store the resulting DDL in a newly created migration.
1. Once you're ready to go to production, you will be able to run `npm run payload migrate` against your production database, which will apply any new migrations that have not yet run.
1. Now your production database is in sync with your Payload config!
<Banner type="warning">
Warning: do not mix "push" and migrations with your local development database. If you use "push" locally, and then try to migrate, Payload will throw a warning, telling you that these two methods are not meant to be used interchangeably.
</Banner>
For more information about migrations, [click here](/docs/beta/database/migrations#when-to-run-migrations).
To use Payload with SQLite, install the package `@payloadcms/db-sqlite`. It leverages Drizzle ORM and `libSQL` to interact with a SQLite database that you provide.
It automatically manages changes to your database for you in development mode, and exposes a full suite of migration controls for you to leverage in order to keep other database environments in sync with your schema. DDL transformations are automatically generated.
To configure Payload to use SQLite, pass the `sqliteAdapter` to your Payload Config as follows:
```ts
import { sqliteAdapter } from '@payloadcms/db-sqlite'
| `client` \* | [Client connection options](https://orm.drizzle.team/docs/get-started-sqlite#turso) that will be passed to `createClient` from `@libsql/client`. |
| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |
| `migrationDir` | Customize the directory that migrations are stored. |
| `logger` | The instance of the logger to be passed to drizzle. By default Payload's will be used. |
| `transactionOptions` | A SQLiteTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) |
| `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '_locales'. |
| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '_rels'. |
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |
## Access to Drizzle
After Payload is initialized, this adapter will expose the full power of Drizzle to you for use if you need it.
You can access Drizzle as follows:
```text
payload.db.drizzle
```
## Tables and relations
In addition to exposing Drizzle directly, all of the tables and Drizzle relations are exposed for you via the `payload.db` property as well.
- Tables - `payload.db.tables`
- Relations - `payload.db.relations`
## Prototyping in development mode
Drizzle exposes two ways to work locally in development mode.
The first is [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push), which automatically pushes changes you make to your Payload Config (and therefore, Drizzle schema) to your database so you don't have to manually migrate every time you change your Payload Config. This only works in development mode, and should not be mixed with manually running [`migrate`](/docs/database/migrations) commands.
You will be warned if any changes that you make will entail data loss while in development mode. Push is enabled by default, but you can opt out if you'd like.
Alternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload Config.
## Migration workflows
In SQLite, migrations are a fundamental aspect of working with Payload and you should become familiar with how they work.
For more information about migrations, [click here](/docs/beta/database/migrations#when-to-run-migrations).
Since Payload hooks can be async and be written to not await the result, it is possible to have an incorrect success response returned on a request that is rolled back. If you have a hook where you do not `await` the result, then you should **not** pass the `req.transactionID`.
When writing your own scripts or custom endpoints, you may wish to have direct control over transactions. This is useful for interacting with your database outside of Payload's local API.
@@ -66,4 +67,8 @@ The following functions can be used for managing transactions:
`payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls.
`payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.
`payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
`payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
## Disabling Transactions
If you wish to disable transactions entirely, you can do so by passing `false` as the `transactionOptions` in your database adapter configuration. All the official Payload database adapters support this option.
desc: Payload uses NodeMailer to allow you to send emails smoothly from your app. Set up email functions such as password resets, order confirmations and more.
Payload comes ready to send your application's email. Whether you simply need built-in password reset
email to work or you want customers to get an order confirmation email, you're almost there. Payload makes use of
[NodeMailer](https://nodemailer.com) for email and won't get in your way for those already familiar.
Payload has a few email adapters that can be imported to enable email functionality. The [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) package will be the package most will want to install. This package provides an easy way to use [Nodemailer](https://nodemailer.com) for email and won't get in your way for those already familiar.
For email to send from your Payload server, some configuration is required. The settings you provide will be set
in the `email` property object of your payload init call. Payload will make use of the transport that you have configured for it for things like reset password or verifying new user accounts and email send methods are available to you as well on your payload instance.
The email adapter should be passed into the `email` property of the Payload Config. This will allow Payload to send [auth-related emails](../authentication/email) for things like password resets, new user verification, and any other email sending needs you may have.
### Configuration
## Configuration
**Three ways to set it up**
### Default Configuration
1. **Default**: When email is not needed, a mock email handler will be created and used when nothing is provided. This is ideal for development environments and can be changed later when ready to [go to production](/docs/production/deployment).
1. **Recommended**: Set the `transportOptions` and Payload will do the set up for you.
1. **Advanced**: The `transport` object can be assigned a nodemailer transport object set up in your server scripts and given for Payload to use.
When email is not needed or desired, Payload will log a warning on startup notifying that email is not configured. A warning message will also be logged on any attempt to send an email.
The following options are configurable in the `email` property object as part of the options object when calling payload.init().
| **`fromName`** \* | The name part of the From field that will be seen on the delivered email |
| **`fromAddress`** \* | The email address part of the From field that will be used when delivering email |
| **`transport`** | The NodeMailer transport object for when you want to do it yourself, not needed when transportOptions is set |
| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [NodeMailer documentation](https://nodemailer.com/smtp/) or see the examples below |
| **`logMockCredentials`** | If set to true and no transport/transportOptions, ethereal credentials will be logged to console on startup |
An email adapter will require at least the following fields:
_\* An asterisk denotes that a property is required._
| Nodemailer | [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) | Use any [Nodemailer transport](https://nodemailer.com/transports), including SMTP, Resend, SendGrid, and more. This was provided by default in Payload 2.x. This is the easiest migration path. |
| Resend | [@payloadcms/email-resend](https://www.npmjs.com/package/@payloadcms/email-resend) | Resend email via their REST API. This is preferred for serverless platforms such as Vercel because it is much more lightweight than the nodemailer adapter. |
| **`transport`** | The Nodemailer transport object for when you want to do it yourself, not needed when transportOptions is set |
| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [Nodemailer documentation](https://nodemailer.com) or see the examples below |
## Use SMTP
Simple Mail Transfer Protocol (SMTP) options can be passed in using the `transportOptions` object on the `email` options. See the [Nodemailer SMTP documentation](https://nodemailer.com/smtp/) for more information, including details on when `secure` should and should not be set to `true`.
**Example email options using SMTP:**
```ts
payload.init({
email: {
import { buildConfig } from 'payload'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'info@payloadcms.com',
defaultFromName: 'Payload',
// Nodemailer transportOptions
transportOptions: {
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
port: 587,
secure: true, // use TLS
tls: {
// do not fail on invalid certs
rejectUnauthorized: false,
},
},
fromName: 'hello',
fromAddress: 'hello@example.com',
},
// ...
}),
})
```
<Banner type="warning">
It is best practice to avoid saving credentials or API keys directly in your code, use
Many third party mail providers are available and offer benefits beyond basic SMTP. As an example your payload init could look this if you wanted to use SendGrid.com though the same approach would work for any other [NodeMailer transports](https://nodemailer.com/transports/) shown here or provided by another third party.
**Example email options using nodemailer.createTransport:**
```ts
import payload from 'payload'
import nodemailerSendgrid from 'nodemailer-sendgrid'
To take full control of the mail transport you may wish to use `nodemailer.createTransport()` on your server and provide it to Payload init.
```ts
import payload from 'payload'
import { buildConfig } from 'payload'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import nodemailer from 'nodemailer'
const payload = require('payload')
const nodemailer = require('nodemailer')
const transport = await nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
})
payload.init({
email: {
fromName: 'Admin',
fromAddress: 'admin@example.com',
transport,
},
// ...
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'info@payloadcms.com',
defaultFromName: 'Payload',
// Any Nodemailer transport can be used
transport: nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
}),
}),
})
```
### Sending Mail
**Custom Transport:**
With a working transport you can call it anywhere you have access to payload by calling `payload.sendEmail(message)`. The `message` will contain the `to`, `subject` and `email` or `text` for the email being sent. To see all available message configuration options see [NodeMailer](https://nodemailer.com/message).
You also have the ability to bring your own nodemailer transport. This is an example of using the SendGrid nodemailer transport.
### Mock transport
By default, Payload uses a mock implementation that only sends mail to the [ethereal](https://ethereal.email) capture service that will never reach a user's inbox. While in development you may wish to make use of the captured messages which is why the payload output during server output helpfully logs this out on the server console.
To see ethereal credentials, add `logMockCredentials: true` to the email options. This will cause them to be logged to console on startup.
```ts
payload.init({
email: {
fromName: 'Admin',
fromAddress: 'admin@example.com',
logMockCredentials: true, // Optional
},
// ...
import { buildConfig } from 'payload'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import nodemailerSendgrid from 'nodemailer-sendgrid'
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'info@payloadcms.com',
defaultFromName: 'Payload',
transportOptions: nodemailerSendgrid({
apiKey: process.env.SENDGRID_API_KEY,
}),
}),
})
```
**Console output when starting payload with a mock email instance and logMockCredentials: true**
During development, if you pass nothing to `nodemailerAdapter`, it will use the [ethereal.email](https://ethereal.email) service.
```
[06:37:21] INFO (payload): Starting Payload...
[06:37:22] INFO (payload): Payload Demo Initialized
[06:37:22] INFO (payload): listening on 3000...
[06:37:22] INFO (payload): Connected to MongoDB server successfully!
[06:37:23] INFO (payload): E-mail configured with mock configuration
[06:37:23] INFO (payload): Log into mock email provider at https://ethereal.email
[06:37:23] INFO (payload): Mock email account username: hhav5jw7doo4euev@ethereal.email
[06:37:23] INFO (payload): Mock email account password: VNdGcvDZeyEhtuPBqf
This will log the ethereal.email details to console on startup.
```ts
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
export default buildConfig({
email: nodemailerAdapter(),
})
```
The mock email handler is used when payload is started with neither `transport` or `transportOptions` to know how to deliver email.
## Resend Configuration
<Banner type="warning">
The randomly generated email account username and password will be different each time the Payload
server starts.
</Banner>
The Resend adapter requires an API key to be passed in the options. This can be found in the Resend dashboard. This is the preferred package if you are deploying on Vercel because this is much more lightweight than the Nodemailer adapter.
### Using multiple mail providers
| Option | Description |
| ------ | ----------------------------------- |
| apiKey | The API key for the Resend service. |
```ts
import { buildConfig } from 'payload'
import { resendAdapter } from '@payloadcms/email-resend'
export default buildConfig({
email: resendAdapter({
defaultFromAddress: 'dev@payloadcms.com',
defaultFromName: 'Payload CMS',
apiKey: process.env.RESEND_API_KEY || '',
}),
})
```
## Sending Mail
With a working transport you can call it anywhere you have access to Payload by calling `payload.sendEmail(message)`. The `message` will contain the `to`, `subject` and `html` or `text` for the email being sent. Other options are also available and can be seen in the sendEmail args. Support for these will depend on the adapter being used.
```ts
// Example of sending an email
const email = await payload.sendEmail({
to: 'test@example.com',
subject: 'This is a test email',
text: 'This is my message body',
})
```
## Using multiple mail providers
Payload supports the use of a single transporter of email, but there is nothing stopping you from having more. Consider a use case where sending bulk email is handled differently than transactional email and could be done using a [hook](/docs/hooks/overview).
keywords: example, examples, starter, boilerplate, template, templates
---
Payload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated so you can easily decipher what is going on.
Examples are changing every day, so be sure to check back often to see what new examples have been added. If you have a specific example you would like to see, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
When necessary, some examples include a front-end. Examples that require a front-end share this folder structure:
```plaintext
example/
├── payload/
├── next-app/
├── next-pages/
├── react-router/
├── vue/
├── svelte/
```
Where `payload` is your Payload project, and the other directories are dedicated to their respective front-end framework. We are adding new examples every day, so if your framework of choice is not yet supported in any particular example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.