Compare commits

..

60 Commits

Author SHA1 Message Date
James
1c896eb400 chore: cleanup 2025-01-28 11:13:02 -05:00
James
cb7edda17c chore: improves cloud autorun logic 2025-01-28 11:11:55 -05:00
Jessica Chowdhury
9c31a52329 fix: checks for localization to prevent publish button breaking (#10844)
This [PR](https://github.com/payloadcms/payload/pull/9438) introduced a
bug with the publish button, an error was being thrown when localization
is false. Updated the logic breaking the publish button to safely check
whether localization exists.
2025-01-28 14:39:29 +00:00
Jessica Chowdhury
9b497414fe feat: allow publish and publish specific locale buttons to be swapped (#9438)
### What?
Adds new feature to change the default behavior of the Publish button.

When localization is enabled, you can now choose whether to publish all
locales (default) or publish the active locale only.

<img width="401" alt="Screenshot 2024-11-22 at 12 03 20 PM"
src="https://github.com/user-attachments/assets/757383b9-3bf9-4816-8223-a907b120912e">

### Why?

Since implementing the ability to publish a specific locale, users have
reported that having this option as the default button would be
preferred in some cases.

### How?

Add `defaultLocalePublishOption` to your localization config and set it
to 'active':

```ts
  localization: {
    defaultLocalePublishOption: 'active',
    // the rest of your localization config
  },
```

---------

Co-authored-by: Anders Semb Hermansen <anders.hermansen@gmail.com>
2025-01-28 11:24:07 +00:00
Said Akhrarov
8952662db9 docs: fix links and formatting (#10835)
### What?
This PR fixes many links in the docs as well as a few formatting and
grammar issues.

### Why?
To properly link users to the correct destination in the docs and
present well-formatted docs.

### How?
Changes to a few files in `docs/`
2025-01-27 22:50:54 -07:00
Jarrod Flesch
a835518232 fix(ui): adds prev value on form state validat functions (#10832)
### What?
When hitting the form-state endpoint, previousValue was undefined.

### Why?
It was not being passed through.

### How?
Sets previousValue as the initial value of the field.

Fixes https://github.com/payloadcms/payload/issues/10826
2025-01-27 22:08:34 +00:00
Jacob Fletcher
82f1bb9864 perf: skips field validations until the form is submitted (#10580)
Field validations can be expensive, especially custom validations that
are async or highly complex. This can lead to slow form state response
times when generating form state for many such fields. Ideally, we only
run validations on fields whose values have changed. This is not
possible, however, because field validation functions might reference
_other_ field values with their args, and there is no good way of
detecting exactly which fields should run in this case. The next best
thing here is to only run validations _after the form has been
submitted_, and then every `onChange` event thereafter until a
successful submit has taken place. This is an elegant solution because
we currently don't _render_ field errors until submission anyway.

This change will significantly speed up form state response times, at
least until the form has been submitted. From then on, all field
validations will run regardless, just as they do now. If custom
validations continue to slow down form state response times, there is a
new `event` arg introduced in #10738 that can be used to control whether
heavy operations occur on change or on submit.

Related: #10638
2025-01-27 20:21:33 +00:00
Jacob Fletcher
0acaf8a7f7 fix: field paths within hooks (#10638)
Field paths within hooks are not correct.

For example, an unnamed tab containing a group field and nested text
field should have the path:
- `myGroupField.myTextField`

However, within hooks that path is formatted as:
- `_index-1.myGroupField.myTextField`

The leading index shown above should not exist, as this field is
considered top-level since it is located within an unnamed tab.

This discrepancy is only evident through the APIs themselves, such as
when creating a request with invalid data and reading the validation
errors in the response. Form state contains proper field paths, which is
ultimately why this issue was never caught. This is because within the
admin panel we merge the API response with the current form state,
obscuring the underlying issue. This becomes especially obvious in
#10580, where we no longer initialize validation errors within form
state until the form has been submitted, and instead rely solely on the
API response for the initial error state.

Here's comprehensive example of how field paths _should_ be formatted:

```
{
  // ...
  fields: [
    {
      // path: 'topLevelNamedField'
      // schemaPath: 'topLevelNamedField'
      // indexPath: ''
      name: 'topLevelNamedField',
      type: 'text',
    },
    {
      // path: 'array'
      // schemaPath: 'array'
      // indexPath: ''
      name: 'array',
      type: 'array',
      fields: [
        {
          // path: 'array.[n].fieldWithinArray'
          // schemaPath: 'array.fieldWithinArray'
          // indexPath: ''
          name: 'fieldWithinArray',
          type: 'text',
        },
        {
          // path: 'array.[n].nestedArray'
          // schemaPath: 'array.nestedArray'
          // indexPath: ''
          name: 'nestedArray',
          type: 'array',
          fields: [
            {
              // path: 'array.[n].nestedArray.[n].fieldWithinNestedArray'
              // schemaPath: 'array.nestedArray.fieldWithinNestedArray'
              // indexPath: ''
              name: 'fieldWithinNestedArray',
              type: 'text',
            },
          ],
        },
        {
          // path: 'array.[n]._index-2'
          // schemaPath: 'array._index-2'
          // indexPath: '2'
          type: 'row',
          fields: [
            {
              // path: 'array.[n].fieldWithinRowWithinArray'
              // schemaPath: 'array._index-2.fieldWithinRowWithinArray'
              // indexPath: ''
              name: 'fieldWithinRowWithinArray',
              type: 'text',
            },
          ],
        },
      ],
    },
    {
      // path: '_index-2'
      // schemaPath: '_index-2'
      // indexPath: '2'
      type: 'row',
      fields: [
        {
          // path: 'fieldWithinRow'
          // schemaPath: '_index-2.fieldWithinRow'
          // indexPath: ''
          name: 'fieldWithinRow',
          type: 'text',
        },
      ],
    },
    {
      // path: '_index-3'
      // schemaPath: '_index-3'
      // indexPath: '3'
      type: 'tabs',
      tabs: [
        {
          // path: '_index-3-0'
          // schemaPath: '_index-3-0'
          // indexPath: '3-0'
          label: 'Unnamed Tab',
          fields: [
            {
              // path: 'fieldWithinUnnamedTab'
              // schemaPath: '_index-3-0.fieldWithinUnnamedTab'
              // indexPath: ''
              name: 'fieldWithinUnnamedTab',
              type: 'text',
            },
            {
              // path: '_index-3-0-1'
              // schemaPath: '_index-3-0-1'
              // indexPath: '3-0-1'
              type: 'tabs',
              tabs: [
                {
                  // path: '_index-3-0-1-0'
                  // schemaPath: '_index-3-0-1-0'
                  // indexPath: '3-0-1-0'
                  label: 'Nested Unnamed Tab',
                  fields: [
                    {
                      // path: 'fieldWithinNestedUnnamedTab'
                      // schemaPath: '_index-3-0-1-0.fieldWithinNestedUnnamedTab'
                      // indexPath: ''
                      name: 'fieldWithinNestedUnnamedTab',
                      type: 'text',
                    },
                  ],
                },
              ],
            },
          ],
        },
        {
          // path: 'namedTab'
          // schemaPath: '_index-3.namedTab'
          // indexPath: ''
          label: 'Named Tab',
          name: 'namedTab',
          fields: [
            {
              // path: 'namedTab.fieldWithinNamedTab'
              // schemaPath: '_index-3.namedTab.fieldWithinNamedTab'
              // indexPath: ''
              name: 'fieldWithinNamedTab',
              type: 'text',
            },
          ],
        },
      ],
    },
  ]
}
```
2025-01-27 14:41:35 -05:00
Jarrod Flesch
9f9919d2c6 fix(next): remove toString coercion inside getDocumentPermissions (#10828)
### What?
When the doc permissions were retrieved from the DB, we were coercing
them into strings even when they should not have been.

### Why?
Usage of `id.toString()`

### How?
Remove `id.toString()`. The id will be correct by this point and we
should never coerce id's like this.

Fixes https://github.com/payloadcms/payload/issues/8218
2025-01-27 14:16:33 -05:00
Tib
95e81d8d96 docs: fix typo (#10824)
Fixing small typo
2025-01-27 19:08:37 +00:00
Jessica Chowdhury
ffe8020916 fix(translations): adds et to import file (#10823)
### What?
Error thrown when initiating the Estonian language (`et`) from
`@payloadcms/translations`
<img width="896" alt="Screenshot 2025-01-27 at 3 17 49 PM"
src="https://github.com/user-attachments/assets/27603c75-d713-4f11-b141-dc293d51800c"
/>

### How?
`et` needed to be added to `importDateFNSLocale`. Tested after this
change and the error is no longer present.

Fixes #10817
2025-01-27 15:40:24 +00:00
felismargarita
0d81ff2f59 fix(ui): hide the restore button's empty submenu in a draft version (#10756)
### What?
When the draft functionality is enabled, in the version history page,
_Restore this version_ renders an empty submenu popup.
### Why?
Restore this version button has below 2 basic scenarios:
1. restore a previously published version. 
In this scenario, this button has a second option _Restore as a draft_
which is in the button submenu area, it works perfectly.
2. restore a draft version
As a draft version, there should not have a second option _Restore as a
draft_, because it is a draft version itself, so this second option and
the submenu is not required in this scenario, but actually it shows an
empty submenu which is not a good idea
### How?
Check if this version is a draft version, if yes, no need to set a
render prop to the button component, so the empty popup won't be
displayed.

Fixes #10754

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2025-01-27 10:31:36 -05:00
Germán Jabloñski
d6658b55a1 chore(templates): fix: the contact page of the website template throws an error in live preview (#10785)
Fixes: https://github.com/payloadcms/payload/issues/10787

The underlying problem is that there are types in the form builder
plugin that are unnecessarily `any` or `unknown`.
Here in the website template this was being circumvented with a function
that was not really needed (buildInitialFormState), and with new unknown
types (Value, Property and Data).

Since create-payload-app fetches from the latest commit instead of the
latest release, it is necessary to first merge
https://github.com/payloadcms/payload/pull/10789, and once the next
release is done this PR can be merged.
2025-01-27 15:26:36 +00:00
Said Akhrarov
8289588994 fix(ui): include check for parent permissions in renderField (#10729)
### What?
This PR fixes an issue for tab subfield permissions

### Why?
Permissions were not being correctly extracted when passing them down.

### How?
Properly extracts permissions when rendering fields inside the active tab.

Fixes #10720

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2025-01-27 09:45:10 -05:00
Jessica Chowdhury
7a398704a0 fix(plugin-nested-docs): update draft and published child docs on resave (#10454)
### What?
Fixes bug with **plugin-nested-docs**. The plugin should update the
breadcrumb data of any child documents when the parent doc is updated,
currently only the **draft** child document is updated.

### How?
Updates the resave function to fetch draft and published child docs.

Closes issue #10066
2025-01-27 13:28:15 +00:00
Wallerand Delevacq
c1c64a07a2 fix(plugin-multi-tenant): issue #10740 - "The following field is invalid: Assigned Tenant" (#10764)
### What?
Tenant ID is a `number` but the `beforeChange` hook shows it as a `string`.
Getting tenant ID from cookie does not work properly in PG

### Why?
A `ValidationError` is throwing when reading a pg numeric id from the cookie.

### How?
Adjust the id based on the idType on the collection.

Fixes #10740

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2025-01-24 22:29:49 -05:00
Germán Jabloñski
6a39279697 docs: improvements in rich text section (#9954)
- fixed broken links
- improved introduction of `overview` and `slate` sections
2025-01-24 17:33:43 -03:00
James Mikrut
ace755742c chore: improves routeError log safety (#10793)
Improves the logging that `routeError` throws.

Logs were sometimes being swallowed if there was a problem with the
incoming request. Now they will surface.
2025-01-24 19:06:01 +00:00
Francisco Lourenço
828b3b71c0 feat: allows fields to be collapsed in the version view diff (#8054)
## Description

Allows some fields to be collapsed in the version diff view. The fields
that can be collapsed are the ones which can also be collapsed in the
edit view, or that have visual grouping:
- `collapsible` 
- `group`
- `array`  (and their rows)
- `blocks` (and their rows)
- `tabs`

It also 
- Fixes incorrect indentation of some fields
- Fixes the rendering of localized tabs in the diff view
- Fixes locale labels for the group field
- Adds a field change count to each collapsible diff (could imagine this
being used in other places)
- Brings the indentation gutter back to help visualize multiple nesting
levels


## Future improvements
- Persist collapsed state across page reloads (sessionStorage vs
preferences)

## Screenshots

### Without locales

![comparison](https://github.com/user-attachments/assets/754be708-be6d-43b4-bbe3-5d64ab6a0f76)


### With locales
![comparison with
locales](https://github.com/user-attachments/assets/02fb47fb-fa38-4195-8376-67bfda7f282d)

-------------- 

- [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)

## 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~
2025-01-24 13:32:55 -05:00
Germán Jabloñski
92e6beb050 fix(plugin-form-builder): type of MessageField to object (#10792)
Modifying https://github.com/payloadcms/payload/pull/10789 to make
MessageField editor agnostic (i.e. works with Lexical or Slate or any
other editor)
2025-01-24 18:05:52 +00:00
Elliot DeNolf
eca4f47d7a ci: remove docker login, not functional for external contributors 2025-01-24 12:51:21 -05:00
Alessio Gravili
72a5c02d95 chore: uncomment collectionSlugs array in fields test suite, for resetDB to work properly (#10778)
This array was commented out as a remnant of the large on-demand RSC PR,
in which we commented out substantial portions of code
2025-01-24 10:01:20 -07:00
Germán Jabloñski
5603c1ce8d fix(plugin-form-builder): type of MessageField to SerializedEditorState (#10789)
Fixes https://github.com/payloadcms/payload/issues/10787

This is the first part of the fix for
https://github.com/payloadcms/payload/issues/10787.
The second part is https://github.com/payloadcms/payload/pull/10785

The `packages/plugin-form-builder/src/types.ts` file contains many more
types besides MessageField.message that are unnecessarily `unknown` or
`any`.

In this PR I'm only fixing that one because:
1. It's what's needed to fix the issue.
2. I want to avoid a breaking change (they should be improved in v4
though).

I don't think MessageField.message will cause a breaking change for
anyone, since it's based on a rich text field.
2025-01-24 15:22:08 +00:00
Jarrod Flesch
22633a6de6 fix(plugin-multi-tenant): remove tenant cookie on logout (#10761)
### What?
- Removes the tenant cookie when the user logs out
- Prevents double redirect to globals when no tenant is selected

### Why?
There were a couple scenarios where the cookie and the tenant did not
match, ie if you logged into 1 tenant, and then out and then into
another tenant.
2025-01-24 10:10:49 -05:00
Elliot DeNolf
d6ae07dec6 ci: update CODEOWNERS 2025-01-24 08:59:59 -05:00
Alessio Gravili
344b23139e ci: fix run e2e command (#10779) 2025-01-24 03:47:49 +00:00
Alessio Gravili
b9d3250117 chore: migrate outdated @payloadcms/next/utilities imports (#10777) 2025-01-24 01:58:45 +00:00
Alessio Gravili
03f7bdf1ee chore: disable bun run test buttons if bun extension is installed (#10775)
If the bun extension is installed, a "Run Test" button is displayed in
int test files. Clicking it will use bun to run those tests, which will
always fail.

This PR disables that test button, as it's useless in our repo

![CleanShot 2025-01-23 at 17 39
03@2x](https://github.com/user-attachments/assets/918fa729-8076-4214-a3d2-f824a4fbfc34)

The real button here is "Run". Clicking "Run Test" instead will use bun
and fail. Confusing, right?
2025-01-24 00:41:06 +00:00
Alessio Gravili
59545b5fe5 templates: ensure lexical link validation does not break for internal links (#10771) 2025-01-23 22:42:10 +00:00
Germán Jabloñski
b76401c101 chore(templates): fix eslint errors in plugin template (#10770)
Additionally, the scope of `pnpm eslint` has been expanded to cover the
entire project, not only src.
2025-01-23 21:01:54 +00:00
Jacob Fletcher
a05240a853 perf: only validate filter options on submit (#10738)
Field validations currently run very often, such as within form state on
type. This can lead to serious performance implications within the admin
panel if those validation functions are async, especially if they
perform expensive database queries. One glaring example of this is how
all relationship and upload fields perform a database lookup in order to
evaluate that the given value(s) satisfy the defined filter options. If
the field is polymorphic, this can happen multiple times over, one for
each collection. Similarly, custom validation functions might also
perform expensive tasks, something that Payload has no control over.

The fix here is two-fold. First, we now provide a new `event` arg to all
`validate` functions that allow you to opt-in to performing expensive
operations _only when documents are submitted_, and fallback to
significantly more performant validations as form state is generated.
This new pattern will be the new default for relationship and upload
fields, however, any custom validation functions will need to be
implemented in this way in order to take advantage of it. Here's what
that might look like:

```
[
  // ...
  {
    name: 'text'
    type: 'text',
    validate: async (value, { event }) => {
      if (event === 'onChange') {
        // Do something highly performant here
        return true
      }
      
      // Do something more expensive here
      return true
    }
  }
]
```

The second part of this is to only run validations _after the form as
been submitted_, and then every change event thereafter. This work is
being done in #10580.
2025-01-23 15:10:31 -05:00
Alessio Gravili
9f2bca104b fix(richtext-lexical): afterRead hooks were not awaited (#10747)
This was a tricky one.

Fixes https://github.com/payloadcms/payload/issues/10700

May potentially fix 9163

This could have also been causing glitchyness related to things like
lexical upload / relationship / link node population.

## Issue and solution explained

The lexical field afterRead hook is responsible for executing afterRead
hooks (this includes relationship population) for all sub-nodes, e.g.
upload or block nodes.

Any field and population promises that were created in the process of
traversing the lexical editor state were added to the parent
`fieldPromises` and `populationPromises` array.

Now this lexical `afterRead` hook, which is responsible for creating and
adding all field and population promises of its sub-nodes to the parent
`fieldPromises` and `populationPromises` arrays, was itself part of the
**same** `fieldPromises` array.

The execution of this lexical `afterRead` hook itself is happening while
the `fieldPromises` array is being awaited. This means that new field
and population promises were being added to this same array DURING the
process of awaiting all promises of this array.

As a result, any promises dynamically added while awaiting the initial
set of fieldPromises were not included in the initial `Promise.all()`
awaiting process, leading to unhandled promises.

This PR resolves the issue by ensuring that promises are repeatedly
awaited until no new promises remain in the arrays. By continuously
awaiting the `fieldPromises` and `populationPromises` until all
dynamically added promises are fully resolved, this PR ensures that any
promises added during the processing of a parent promise are also
properly awaited. This guarantees that no promises are skipped,
preserving the intended behavior of the afterRead hook
2025-01-23 14:44:21 -05:00
Said Akhrarov
ec1a441ed7 docs: adds limit, page, sort, where, and joins to list of rest query params (#10751)
<!--

Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.

Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.

The following items will ensure that your PR is handled as smoothly as
possible:

- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Fixes #

-->
### What?
This PR adds a few missing query params to the list of REST query params
available on the rest-api overview page of the docs.

### Why?
To better inform users of more utilities available to them right in the
overview page.

### How?
Changes to `rest-api/overview.mdx`
2025-01-23 14:23:02 -05:00
Elliot DeNolf
b2ebf85082 chore(release): v3.19.0 [skip ci] 2025-01-23 13:38:39 -05:00
Sasha
c0ae994da2 fix: next.js rewrites were not respected for rest api (#10759)
Fixes https://github.com/payloadcms/payload/issues/10655
2025-01-23 13:36:33 -05:00
Germán Jabloñski
5689c6527b chore(templates): fix eslint errors in website template (#10752) 2025-01-23 15:30:20 -03:00
Sasha
2d8ff7218a fix(db-mongodb): v2-v3 migration versions docs of collections and globals without relationship fields (#10755)
Fixes https://github.com/payloadcms/payload/issues/10753
2025-01-23 12:33:43 -05:00
Paul
61a2a9c3a3 templates: update website readmes for additional information on jobs and revalidation (#10758) 2025-01-23 17:33:07 +00:00
Sasha
d601300034 fix(db-mongodb): querying polymorphic relationships with the all operator (#10704)
Fixes https://github.com/payloadcms/payload/issues/10678
2025-01-23 18:23:50 +02:00
Jacob Fletcher
e5b3da972f docs: moves collection and globals admin docs to their respective config overviews (#10743)
Similar to #10742. Collection and global-level admin options are
currently documented within the "admin > collections" and "admin >
globals" pages, respectively. This makes them hard to find because
users, myself included, intuitively navigate to the collection and
global overview docs to locate this information before realizing it
lives elsewhere. Now, they are rendered within "configuration >
collections" and "configuration > globals" as expected and the old pages
have been removed altogether.
2025-01-23 01:54:31 -05:00
Jacob Fletcher
0ca37364ea docs: moves customizing fields doc to fields overview (#10742)
Continuation of #10741. Field-level admin options, including the
conditional logic and custom field components, are currently documented
within the "admin > customizing views" page. This makes them hard to
find because users, myself included, intuitively navigate to the fields
overview doc first to locate this information. Now, they are rendered
within "fields > overview" as expected. This should help keep the user
from jumping around from doc to doc and getting lost.
2025-01-22 23:58:05 -05:00
Jacob Fletcher
8b3e2ff5e5 docs: adds examples of typed custom field components (#10741)
Although the "customizing fields" doc provides a big picture overview of
how to create custom field components, it is not explicit enough for
developers to know exactly where to start. For example, it can be
challenging to import the correct types when building these components,
and the natural place to go looking for this information is on the
fields docs themselves. Now, each field doc has its own dedicated
"custom components" section which provides concrete examples for fields
and field labels in both server and client component format, with more
examples to come over time such as using inputs directly, etc. In the
same vein, the "customizing fields" doc itself should probably be moved
to the fields overview section so it remains as intuitive as possible
when searching for this information.
2025-01-22 23:07:09 -05:00
Dan Ribbens
3501d47e2d fix(plugin-nested-docs): cannot update more than 10 child docs (#10737)
`limit` was defaulting to 10 in the call to find children that need to
be updated, now limit is 0 to get all children docs.
2025-01-22 17:42:12 -05:00
Germán Jabloñski
4aaef5e63e feat(richtext-lexical): make decoratorNodes and blocks selectable. Centralize selection and deletion logic (#10735)
- Blocks can now be selected (only inline blocks were possible before).
- Any DecoratorNode that users create will have the necessary logic out
of the box so that they are selected with a click and deleted with
backspace/delete.
- By having the code for selecting and deleting centralized, a lot of
repetitive code was eliminated
- More performant code due to the use of event delegation. There is only
one listener, previously there was one for each decoratorNode.
- Heuristics to exclude scenarios where you don't want to select the
node: if it is inside the DecoratorNode, but is also inside a button,
input, textarea, contentEditable, .react-select, .code-editor or
.no-select-decorator. That last one was added as a means of opt-out.
- Fix #10634

Note: arrow navigation will be introduced in a later PR.



https://github.com/user-attachments/assets/92f91cad-4f70-4f72-a36f-c68afbe33c0d
2025-01-22 22:28:48 +00:00
Patrik
f181f97d4e fix(ui): filters out upload specific fields for bulk editing (#10726)
### What

This PR adds a filtering mechanism to exclude certain reserved fields
from being displayed in the `Edit Many` drawer for bulk uploads. This
ensures that only relevant fields are available for bulk editing.

### Why

Fields like `filename`, `mimeType`, and `filesize` are not intended to
be edited in bulk. Filtering these fields streamlines the interface and
focuses on fields that are meaningful for bulk operations.

### How

- Introduced a `filterOutUploadFields` utility to exclude reserved
fields from the field selection in bulk uploads.
- Applied this filter to the `Edit Many` drawer, ensuring a more
relevant and user-friendly experience.
- Reserved fields include properties like `file`, `mimeType`, `url`,
`width`, `height`, and others that are not applicable for bulk editing.
2025-01-22 16:44:17 -05:00
Alessio Gravili
0c5321e6f8 chore: temporarily revert 10597 (#10718)
Temporarily reverts 10597 to fix plugin-oauth
2025-01-22 16:36:54 -05:00
Jarrod Flesch
9a8769967c chore(plugin-multi-tenant): test suite enhancements (#10732)
### What?
Updates test suite multi-tenant config as a better example.

### Why?
So it is easier to follow and write logical tests for in the future.
2025-01-22 16:28:18 -05:00
Patrik
be2c482054 feat(ui): adds edit many option for bulk uploads (#10646)
### What?

This PR introduces the ability to bulk edit multiple uploads
simultaneously within the `Edit all` option for bulk uploads. Users can
now select fields to update across all selected uploads in a single
operation.

### Why?

Managing multiple uploads individually can be time-consuming and
inefficient, especially when updating common fields. This feature
streamlines the process, improving user experience and productivity when
handling bulk uploads.

### How?

* Added an `Edit Many` drawer component specific to bulk uploads that
allows users to select fields for bulk editing.
* Enhanced the FormsManager and related logic to ensure updates are
applied consistently across all selected uploads.

![Screenshot 2025-01-21 at 3 16
49 PM](https://github.com/user-attachments/assets/ef1f4a12-95a6-4b21-8efa-5280df0917fc)
2025-01-22 16:20:13 -05:00
Paul
67f7c9513f fix(ui): admin description not being respected on tabs and padding issues with tab descriptions (#10710)
- Fixes collection and tab descriptions not having a bottom padding:
- Deprecates `description` on tabs config in favour of
`admin.description` but supports both
- Adds test to make sure `admin.description` renders with static string
and function too for tabs
2025-01-22 17:17:17 +00:00
Elliot DeNolf
7010d93e94 ci: dockerhub login to increase rate-limit (#10727)
Docker Hub has rate-limits for anonymous users. This adds logging in,
which will increase the limit.

Example error message
```
Unable to find image 'mongo:6' locally
  docker: Error response from daemon: toomanyrequests: 
You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit.
  ```
  
 Example error in pipeline: https://github.com/payloadcms/payload/actions/runs/12911899766/job/36005722765?pr=10723
2025-01-22 17:12:04 +00:00
Paul
9bb27afaf5 fix(richtext-lexical): improved regex matchers for absolute and relative URLs to make autolinking more reliable (#10725)
Fixes an issue where pasting text over a selection will automatically
add it as a link instead of replacing the text. This is caused by poor
regex matching in the case of relative URLs.

Added tests and strengthened both absolute and relative URL matchers
2025-01-22 16:53:56 +00:00
Jarrod Flesch
e6d02600e1 fix(plugin-multi-tenant): selected tenant could become incorrect when navigating out of doc (#10723)
### What?
When switching tenants from within a document and then navigating back
out to the list view, the tenant would not be set correctly.

### Why?
This was because we handle the tenant selector selection differently
when viewing a document.

### How?
Now when you navigate out, the page will refresh the cookie.

Also adds test suite config that shows how the dom can be used to
manipulate styles per tenant.
2025-01-22 11:37:18 -05:00
Alessio Gravili
c1b912d5e5 fix: browser validation error when visiting account page (#10717)
Fixes error when visiting account page:

![CleanShot 2025-01-21 at 23 58
48@2x](https://github.com/user-attachments/assets/01502702-319f-46fd-9197-b345eab7fc86)
2025-01-22 07:19:06 +00:00
Said Akhrarov
d8682f2147 docs: adds info on useSelection and useStepNav hooks (#10683)
### What?
This PR adds information and examples on the `useSelection` and
`useStepNav` hooks.

### Why?
To provide more information on the React hooks available to end-users.

### How?
Changes to `admin/hooks.mdx`
2025-01-22 00:11:47 -07:00
Boyan Bratvanov
6d43910018 docs: multi-tenant plugin - remove @beta and fix npm url (#10697)
Running `pnpm add @payloadcms/plugin-multi-tenant@beta` will install
`v.0.0.1` which is outdated:

https://www.npmjs.com/package/@payloadcms/plugin-multi-tenant?activeTab=versions

I suspect the intention was to remove `@beta` from the plugin install
instructions with yesterday's Payload `v3.18.0` release which also
bumped `plugin-multi-tenant` to `v3.18.0`, but this might have been
missed.
2025-01-22 00:08:38 -07:00
Paul
5e4a1d48ae templates: fix potential error in the initial form state caused by type mismatch (#10713) 2025-01-21 15:34:21 -06:00
David Murdoch
b55342d9af chore(examples): change to useClickableCard to use AbortController (#10680)
* introduce AbortController to the event listeners in useClickableCard
* in an attempt to avoid ugly boolean check
* suggested pattern from
https://kettanaito.com/blog/dont-sleep-on-abort-controller

<!--

Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.

Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.

The following items will ensure that your PR is handled as smoothly as
possible:

- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

Add AbortController to event listeners in useClickableCard.

### Why?

Following Theo's [video](https://www.youtube.com/watch?v=2sdXSczmvNc)
about the [blog
post](https://kettanaito.com/blog/dont-sleep-on-abort-controller) about
AbortController I came across a bit of code in examples that looked a
bit ugly and thought the AbortController could simplify it a bit
(especially the checks for the DOM node in the removal part).

### How?

Just re-writing the code in a different way. Though, I admit I'm not
wholly sure where the Cards are being used and for what purpose so I
haven't checked that there is no difference to the behaviour of the
code.

Fixes #
Not so much a fix but a different way to write the removal of event
listeners.
-->
2025-01-21 15:34:11 -06:00
Jarrod Flesch
2a98c8445e fix(plugin-multi-tenant): corrects user type in userHasAccessToAllTenants fn (#10707)
### What?
User type was inflexible.

### How?
Internally use as unknown.
2025-01-21 12:44:37 -05:00
Jarrod Flesch
a9c08323cc fix(plugin-multi-tenant): prevent throwing when no user exists (#10699)
### What?
Fixes issue where the provider would throw an error and prevent the
login screen from loading if there was no user.

### Why?
Missing try/catch around tenant find for the provider. (Missed because
test suites have autoLogin: true)

### How?
Adds try/catch around find query.
2025-01-21 12:07:47 -05:00
Jarrod Flesch
3e0baf58c0 chore: adds plugin-multi-tenant scope for pr title (#10706)
### What?
Adds `plugin-multi-tenant` scope for PR titles

### Why?
Cannot scope multi-tenant PR's properly
2025-01-21 12:07:10 -05:00
476 changed files with 17609 additions and 6738 deletions

33
.github/CODEOWNERS vendored
View File

@@ -1,29 +1,26 @@
# Order matters. The last matching pattern takes precedence.
# Approvals are not required currently but may be enabled in the future.
### Package Exports ###
/**/exports/ @denolfe @jmikrut @DanRibbens
**/exports/ @denolfe @jmikrut @DanRibbens
### Packages ###
/packages/plugin-cloud*/src/ @denolfe
/packages/email-*/src/ @denolfe
/packages/storage-*/src/ @denolfe
/packages/create-payload-app/src/ @denolfe
/packages/eslint-*/ @denolfe @AlessioGr
/packages/plugin-cloud*/src/ @denolfe @jmikrut @DanRibbens
/packages/email-*/src/ @denolfe @jmikrut @DanRibbens
/packages/storage-*/src/ @denolfe @jmikrut @DanRibbens
/packages/create-payload-app/src/ @denolfe @jmikrut @DanRibbens
/packages/eslint-*/ @denolfe @jmikrut @DanRibbens @AlessioGr
### Templates ###
/templates/_data/ @denolfe
/templates/_template/ @denolfe
/templates/_data/ @denolfe @jmikrut @DanRibbens
/templates/_template/ @denolfe @jmikrut @DanRibbens
### Build Files ###
/tsconfig.json @denolfe
/**/tsconfig*.json @denolfe
/jest.config.js @denolfe
/**/jest.config.js @denolfe
**/tsconfig*.json @denolfe @jmikrut @DanRibbens @AlessioGr
**/jest.config.js @denolfe @jmikrut @DanRibbens @AlessioGr
### Root ###
/package.json @denolfe
/scripts/ @denolfe
/.husky/ @denolfe
/.vscode/ @denolfe @AlessioGr
/.github/ @denolfe
/package.json @denolfe @jmikrut @DanRibbens
/tools/ @denolfe @jmikrut @DanRibbens
/.husky/ @denolfe @jmikrut @DanRibbens
/.vscode/ @denolfe @jmikrut @DanRibbens @AlessioGr
/.github/ @denolfe @jmikrut @DanRibbens

View File

@@ -361,6 +361,12 @@ jobs:
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
run: pnpm exec playwright install-deps chromium
- name: E2E Tests
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
env:
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
NEXT_TELEMETRY_DISABLED: 1
- uses: actions/upload-artifact@v4
if: always()
with:

View File

@@ -52,5 +52,7 @@
"jestrunner.jestCommand": "pnpm exec cross-env NODE_OPTIONS=\"--no-deprecation\" node 'node_modules/jest/bin/jest.js'",
"jestrunner.debugOptions": {
"runtimeArgs": ["--no-deprecation"]
}
},
// Essentially disables bun test buttons
"bun.test.filePattern": "bun.test.ts"
}

View File

@@ -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/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`.
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/overview#auto-login) 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.

View File

@@ -1,8 +1,8 @@
import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { getPayload } from 'payload'
export const Page = async ({ params, searchParams }) => {
const payload = await getPayloadHMR({
const payload = await getPayload({
config: configPromise,
})
return <div>test ${payload?.config?.collections?.length}</div>

View File

@@ -261,7 +261,7 @@ The following arguments are provided to the `delete` function:
If the Collection is used 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.
To add Admin Access Control to a Collection, use the `admin` property in the [Collection Config](../collections/overview):
To add Admin Access Control to a Collection, use the `admin` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'

View File

@@ -1,183 +0,0 @@
---
title: Collection Admin Config
label: Collections
order: 20
desc:
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
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:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: { // highlight-line
// ...
},
}
```
## Admin Options
The following options are available:
| Option | Description |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`group`** | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
| **`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. A field with `virtual: true` cannot be 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](#custom-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](#custom-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). |
| **`baseListFilter`** | You can define a default base filter for this collection's List view, which will be merged into any filters that the user performs. |
### Custom 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'
export const MyCollection: SanitizedCollectionConfig = {
// ...
admin: {
components: { // highlight-line
// ...
},
},
}
```
The following options are available:
| Path | Description |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **`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. |
| **`edit.Upload`** | Replace the default Upload component with a Custom Component. [Upload](../upload/overview) must be enabled. |
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
<Banner type="success">
**Note:**
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 type { 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` property resolves to a string that points to your front-end application with additional URL parameters. This can be an absolute URL or a relative path.
The preview function receives two arguments:
| Argument | Description |
| --- | --- |
| **`doc`** | The Document being edited. |
| **`ctx`** | An object containing `locale`, `token`, and `req` properties. The `token` is the currently logged-in user's JWT. |
If your application requires a fully qualified URL, such as within deploying to Vercel Preview Deployments, you can use the `req` property to build this URL:
```ts
preview: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` // highlight-line
```
<Banner type="success">
**Note:**
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):
```ts
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
admin: {
// highlight-start
pagination: {
defaultLimit: 10,
limits: [10, 20, 50],
},
// highlight-end
},
}
```
The following options are available:
| Option | Description |
| -------------- | --------------------------------------------------------------------------------------------------- |
| `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 type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
admin: {
// highlight-start
listSearchableFields: ['title', 'slug'],
// highlight-end
},
}
```
<Banner type="warning">
**Tip:**
If you are adding `listSearchableFields`, make sure you index each of these fields so your admin queries can remain performant.
</Banner>

View File

@@ -18,9 +18,9 @@ All Custom Components in Payload are [React Server Components](https://react.dev
There are four main types of Custom Components in Payload:
- [Root Components](#root-components)
- [Collection Components](./collections#custom-components)
- [Global Components](./globals#custom-components)
- [Field Components](./fields#custom-components)
- [Collection Components](../configuration/collections/#custom-components)
- [Global Components](../configuration/globals#custom-components)
- [Field Components](../fields/overview#custom-components)
To swap in your own Custom Component, first 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-components) accordingly.
@@ -185,7 +185,7 @@ Each Custom Component receives the following props by default:
<Banner type="warning">
**Reminder:**
All Custom Components also receive various other props that are specific component being rendered. See [Root Components](#root-components), [Collection Components](./collections#custom-components), [Global Components](./globals#custom-components), or [Field Components](./fields#custom-components) for a complete list of all default props per component.
All Custom Components also receive various other props that are specific component being rendered. See [Root Components](#root-components), [Collection Components](../configuration/collections#custom-components), [Global Components](../configuration/globals#custom-components), or [Field Components](../fields/overview#custom-components) for a complete list of all default props per component.
</Banner>
### Custom Props
@@ -291,7 +291,7 @@ export const MyClientComponent: React.FC = () => {
See [Using Hooks](#using-hooks) for more details.
</Banner>
All [Field Components](./fields) automatically receive their respective Field Config through props.
All [Field Components](../fields/overview#custom-components) automatically receive their respective Field Config through props.
```tsx
import React from 'react'
@@ -462,7 +462,7 @@ Payload also exports its [SCSS](https://sass-lang.com) library for reuse which i
## Root Components
Root Components are those that effect the [Admin Panel](./overview) generally, such as the logo or the main nav.
Root Components are those that affect the [Admin Panel](./overview) generally, such as the logo or the main nav.
To override Root Components, use the `admin.components` property in your [Payload Config](../configuration/overview):
@@ -504,14 +504,14 @@ The following options are available:
<Banner type="success">
**Note:**
You can also use set [Collection Components](./collections#custom-components) and [Global Components](./globals#custom-components) in their respective configs.
You can also use set [Collection Components](../configuration/collections#custom-components) and [Global Components](../configuration/globals#custom-components) in their respective configs.
</Banner>
### Custom Providers
As you add more and more Custom Components to your [Admin Panel](./overview), you may find it helpful to add additional [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context)(s). Payload allows you to inject your own context providers in your app so you can export your own custom hooks, etc.
To add a Custom Provider, use the `admin.components.providers` property in your [Payload Config](../getting-started/overview):
To add a Custom Provider, use the `admin.components.providers` property in your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
@@ -546,5 +546,5 @@ export const useMyCustomContext = () => useContext(MyCustomContext)
```
<Banner type="warning">
**Reminder:**React Context exists only within Client Components. This means they must include the `use client` directive at the top of their files and cannot contain server-only code. To use a Server Component here, simply _wrap_ your Client Component with it.
**Reminder:** React Context exists only within Client Components. This means they must include the `use client` directive at the top of their files and cannot contain server-only code. To use a Server Component here, simply _wrap_ your Client Component with it.
</Banner>

View File

@@ -1,509 +0,0 @@
---
title: Customizing Fields
label: Customizing Fields
order: 60
desc:
keywords:
---
[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](#custom-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">
**Tip:**
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):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: { // highlight-line
// ...
},
}
]
}
```
The following options are available:
| Option | Description |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`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) entirely. |
| **`disableBulkEdit`** | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. Defaults to `true` for UI fields. |
| **`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 Descriptions
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](#description).
To add a Custom Description to a field, use the `admin.description` property in your [Field Config](../fields/overview):
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
description: 'Hello, world!' // highlight-line
},
},
]
}
```
<Banner type="warning">
**Reminder:**
To replace the Field Description with a [Custom Component](./components), use the `admin.components.Description` property. [More details](#description).
</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 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'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
description: ({ t }) => `${t('Hello, world!')}` // highlight-line
},
},
]
}
```
All Description Functions receive the following arguments:
| Argument | Description |
| -------------- | ---------------------------------------------------------------- |
| **`t`** | The `t` function used to internationalize the Admin Panel. [More details](../configuration/i18n) |
<Banner type="info">
**Note:**
If you need to subscribe to live updates within your form, use a Description Component instead. [More details](#description).
</Banner>
## 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.
**Example:**
```ts
{
fields: [
{
name: 'enableGreeting',
type: 'checkbox',
defaultValue: false,
},
{
name: 'greeting',
type: 'text',
admin: {
// highlight-start
condition: (data, siblingData, { user }) => {
if (data.enableGreeting) {
return true
} else {
return false
}
},
// highlight-end
},
},
]
}
```
## Custom Components
Within the [Admin Panel](./overview), fields are represented in three distinct places:
- [Field](#field) - The actual form field rendered in the Edit View.
- [Cell](#cell) - The table cell component rendered in the List View.
- [Filter](#filter) - The filter component rendered in the List View.
To swap in Field Components with your own, use the `admin.components` property in your [Field Config](../fields/overview):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: { // highlight-line
// ...
},
},
}
]
}
```
The following options are available:
| Component | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------------------- |
| **`Field`** | The form field rendered of the Edit View. [More details](#field). |
| **`Cell`** | The table cell rendered of the List View. [More details](#cell). |
| **`Filter`** | The filter component rendered in the List View. [More details](#filter). |
| **`Label`** | Override the default Label of the Field Component. [More details](#label). |
| **`Error`** | Override the default Error of the Field Component. [More details](#error). |
| **`Description`** | Override the default Description of the Field Component. [More details](#description). |
| **`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). |
### Field
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 swap in your own Field Component, use the `admin.components.Field` property in your [Field Config](../fields/overview):
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: {
Field: '/path/to/MyFieldComponent', // highlight-line
},
},
}
]
}
```
_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`](#label), [`Error`](#error), [`beforeInput`](#afterinput-and-beforinput), and [`afterInput`](#afterinput-and-beforinput) properties.
</Banner>
#### Default Props
All Field Components receive the following props by default:
| Property | Description |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`docPreferences`** | An object that contains the [Preferences](./preferences) for the document. |
| **`field`** | In Client Components, this is the sanitized Client Field Config. In Server Components, this is the original Field Config. Server Components will also receive the sanitized field config through the`clientField` prop (see below). |
| **`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. |
| **`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. `posts.myGroup.myArray.myField`. |
| **`indexPath`** | A hyphen-notated string representing the path to the field _within the nearest named ancestor field_, i.e. `0-0` |
In addition to the above props, all Server Components will also receive the following props:
| Property | Description |
| ----------------- | ----------------------------------------------------------------------------- |
| **`clientField`** | The serializable Client Field Config. |
| **`field`** | The Field Config. [More details](../fields/overview). |
| **`data`** | The current document being edited. |
| **`i18n`** | The [i18n](../configuration/i18n) object. |
| **`payload`** | The [Payload](../local-api/overview) class. |
| **`permissions`** | The field permissions based on the currently authenticated user. |
| **`siblingData`** | The data of the field's siblings. |
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
| **`value`** | The value of the field at render-time. |
#### 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:
```tsx
'use client'
import { useField } from '@payloadcms/ui'
export const CustomTextField: React.FC = () => {
const { value, setValue } = useField() // highlight-line
return (
<input
onChange={(e) => setValue(e.target.value)}
value={value}
/>
)
}
```
<Banner type="success">
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 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'
```
### Cell
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 swap in your own Cell Component, use the `admin.components.Cell` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Cell: '/path/to/MyCustomCellComponent', // highlight-line
},
},
}
```
All Cell Components receive the same [Default Field Component Props](#field), plus the following:
| Property | Description |
| ---------------- | ----------------------------------------------------------------- |
| **`link`** | A boolean representing whether this cell should be wrapped in a link. |
| **`onClick`** | A function that is called when the cell is clicked. |
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
### Filter
The Filter Component is the actual input element rendered within the "Filter By" dropdown of the List View used to represent this field when building filters.
To swap in your own Filter Component, use the `admin.components.Filter` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Filter: '/path/to/MyCustomFilterComponent', // highlight-line
},
},
}
```
All Custom Filter Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
### Label
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 swap in your own Label Component, use the `admin.components.Label` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Label: '/path/to/MyCustomLabelComponent', // highlight-line
},
},
}
```
All Custom Label Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
#### TypeScript
When building Custom Label Components, you can import the component types 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'
```
### Description
Alternatively to the [Description Property](#field-descriptions), 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 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'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
Description: '/path/to/MyCustomDescriptionComponent', // highlight-line
}
}
}
]
}
```
All Custom Description Components receive the same [Default Field Component Props](#field).
For details on how to build a Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
#### 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'
```
### Error
The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.
To swap in your own Error Component, use the `admin.components.Error` property in your [Field Config](../fields/overview):
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Error: '/path/to/MyCustomErrorComponent', // highlight-line
},
},
}
```
All Error Components receive the [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
#### TypeScript
When building Custom Error Components, you can import the component types 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'
```
### 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'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
// highlight-start
beforeInput: ['/path/to/MyCustomComponent'],
afterInput: ['/path/to/MyOtherCustomComponent'],
// highlight-end
}
}
}
]
}
```
All `afterInput` and `beforeInput` Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components).

View File

@@ -1,107 +0,0 @@
---
title: Global Admin Config
label: Globals
order: 30
desc:
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
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:
```ts
import { GlobalConfig } from 'payload'
export const MyGlobal: GlobalConfig = {
// ...
admin: { // highlight-line
// ...
},
}
```
## Admin Options
The following options are available:
| Option | Description |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **`group`** | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
| **`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](#custom-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). |
### Custom 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'
export const MyGlobal: SanitizedGlobalConfig = {
// ...
admin: {
components: { // highlight-line
// ...
},
},
}
```
The following options are available:
| Path | Description |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| **`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">
**Note:**
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">
**Note:**
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>

View File

@@ -6,7 +6,7 @@ desc: Make use of all of the powerful React hooks that Payload provides.
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
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.
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/overview#custom-components). With them, you can interface with Payload itself to build just about any type of complex customization you can think of.
<Banner type="warning">
**Reminder:**
@@ -15,7 +15,7 @@ Payload provides a variety of powerful [React Hooks](https://react.dev/reference
## useField
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.
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/overview#custom-components), 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:
@@ -764,6 +764,45 @@ The `useListQuery` hook returns an object with the following properties:
| **`handleWhereChange`** | A method to handle where changes in the List View. |
| **`query`** | The current query that is being used to fetch the data in the List View. |
## useSelection
The `useSelection` hook provides information on the selected rows in the List view as well as helper methods to simplify selection. The `useSelection` hook returns an object with the following properties:
| Property | Description |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`count`** | The number of currently selected rows. |
| **`getQueryParams`** | A function that generates a query string based on the current selection state and optional additional filtering parameters. |
| **`selectAll`** | An enum value representing the selection range: `'allAvailable'`, `'allInPage'`, `'none'`, and `'some'`. The enum, `SelectAllStatus`, is exported for easier comparisons. |
| **`selected`** | A map of document id keys and boolean values representing their selection status. |
| **`setSelection`** | A function that toggles the selection status of a document row. |
| **`toggleAll`** | A function that toggles selection for all documents on the current page or selects all available documents when passed `true`. |
| **`totalDocs`** | The number of total documents in the collection. |
**Example:**
```tsx
'use client'
import { useSelection } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const { count, toggleAll, totalDocs } = useSelection()
// highlight-end
return (
<>
<span>Selected {count} out of {totalDocs} docs!</span>
<button
type="button"
onClick={() => toggleAll(true)}
>
Toggle All Selections
</button>
</>
)
}
```
## useLocale
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:
@@ -948,3 +987,32 @@ const ListenForUpdates: React.FC = () => {
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.
</Banner>
## useStepNav
The `useStepNav` hook provides a way to change the step-nav breadcrumb links in the app header.
| Property | Description |
| ---------------- | --------------------------------------------------------------------------------- |
| **`setStepNav`** | A state setter function which sets the `stepNav` array. |
| **`stepNav`** | A `StepNavItem` array where each `StepNavItem` has a label and optionally a url. |
**Example:**
```tsx
'use client'
import { type StepNavItem, useStepNav } from '@payloadcms/ui'
import { useEffect } from 'react'
export const MySetStepNavComponent: React.FC<{
nav: StepNavItem[]
}> = ({ nav }) => {
const { setStepNav } = useStepNav()
useEffect(() => {
setStepNav(nav)
}, [setStepNav, nav])
return null
}
```

View File

@@ -58,7 +58,7 @@ The following options are available for Root Metadata:
<Banner type="success">
**Reminder:**
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.
These are the _root-level_ options for the Admin Panel. You can also customize metadata on the [Collection](../configuration/collections), [Global](../configuration/globals), and Document levels through their respective configs.
</Banner>
### Icons

View File

@@ -103,7 +103,7 @@ The following options are available:
<Banner type="success">
**Reminder:**
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.
These are the _root-level_ options for the Admin Panel. You can also customize [Collection Admin Options](../configuration/collections#admin-options) and [Global Admin Options](../configuration/globals#admin-options) through their respective `admin` keys.
</Banner>
### The Admin User Collection

View File

@@ -143,7 +143,7 @@ The above example shows how to add a new [Root View](#root-views), but the patte
Collection Views are views that are scoped under the `/collections` route, such as the Collection List and Document Edit views.
To 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):
To 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](../configuration/collections):
```ts
import type { SanitizedCollectionConfig } from 'payload'
@@ -248,7 +248,7 @@ The following options are available:
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 swap out Document Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views.Edit[key]` property in your [Collection Config](../collections/overview) or [Global Config](../configuration/globals):
To swap out Document Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views.Edit[key]` property in your [Collection Config](../configuration/collections) or [Global Config](../configuration/globals):
```ts
import type { SanitizedCollectionConfig } from 'payload'

View File

@@ -312,7 +312,7 @@ Payload comes with built-in forgot password functionality. Submitting an email a
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 [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).
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/email#forgot-password).
**Example REST API Forgot Password**:
@@ -348,7 +348,9 @@ const token = await payload.forgotPassword({
})
```
**Note:** if you do not have a `config.serverURL` set, Payload will attempt to create one for you if the `forgot-password` operation was triggered via REST or GraphQL by looking at the incoming `req`. But this is not supported if you are calling `payload.forgotPassword()` via the Local API. If you do not have a `serverURL` set, you may want to override your `auth.forgotPassword.generateEmailHTML` function to provide a full URL to link the user to a proper reset-password page.
<Banner type="info">
**Note:** if you do not have a `config.serverURL` set, Payload will attempt to create one for you if the `forgot-password` operation was triggered via REST or GraphQL by looking at the incoming `req`. But this is not supported if you are calling `payload.forgotPassword()` via the Local API. If you do not have a `serverURL` set, you may want to override your `auth.forgotPassword.generateEmailHTML` function to provide a full URL to link the user to a proper reset-password page.
</Banner>
<Banner type="success">
**Tip:**

View File

@@ -132,7 +132,7 @@ If set to `true`, an email address is required when creating a new user. If set
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):
To enable auto-login, set the `autoLogin` property in the [Payload Config](../admin/overview#admin-options):
```ts
import { buildConfig } from 'payload'

View File

@@ -32,7 +32,7 @@ export default buildConfig({
## Config Options
It's often best practice to write your Collections in separate files and then import them into the main [Payload Config](../overview).
It's often best practice to write your Collections in separate files and then import them into the main [Payload Config](./overview).
Here is what a simple Collection Config might look like:
@@ -59,7 +59,7 @@ The following options are available:
| Option | Description |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`admin`** | The configuration options for the Admin Panel. [More details](../admin/collections). |
| **`admin`** | The configuration options for the Admin Panel. [More details](#admin-options). |
| **`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) |
@@ -93,9 +93,181 @@ Fields define the schema of the Documents within a Collection. To learn more, go
[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.
### Admin Options
## Admin Options
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.
The behavior of Collections within the [Admin Panel](../admin/overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](../admin/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:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
admin: { // highlight-line
// ...
},
}
```
The following options are available:
| Option | Description |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`group`** | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
| **`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. A field with `virtual: true` cannot be 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](#custom-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](../admin/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](#custom-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). |
| **`baseListFilter`** | You can define a default base filter for this collection's List view, which will be merged into any filters that the user performs. |
### Custom Components
Collections can set their own [Custom Components](../admin/components) which only apply to Collection-specific UI within the [Admin Panel](../admin/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:
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollection: SanitizedCollectionConfig = {
// ...
admin: {
components: { // highlight-line
// ...
},
},
}
```
The following options are available:
| Path | Description |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **`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. |
| **`edit.Upload`** | Replace the default Upload component with a Custom Component. [Upload](../upload/overview) must be enabled. |
| **`views`** | Override or create new views within the Admin Panel. [More details](../admin/views). |
<Banner type="success">
**Note:**
For details on how to build Custom Components, see [Building Custom Components](../admin/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:
```ts
import type { 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` property resolves to a string that points to your front-end application with additional URL parameters. This can be an absolute URL or a relative path.
The preview function receives two arguments:
| Argument | Description |
| --- | --- |
| **`doc`** | The Document being edited. |
| **`ctx`** | An object containing `locale`, `token`, and `req` properties. The `token` is the currently logged-in user's JWT. |
If your application requires a fully qualified URL, such as within deploying to Vercel Preview Deployments, you can use the `req` property to build this URL:
```ts
preview: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` // highlight-line
```
<Banner type="success">
**Note:**
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:
```ts
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
admin: {
// highlight-start
pagination: {
defaultLimit: 10,
limits: [10, 20, 50],
},
// highlight-end
},
}
```
The following options are available:
| Option | Description |
| -------------- | --------------------------------------------------------------------------------------------------- |
| `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:
```ts
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
admin: {
// highlight-start
listSearchableFields: ['title', 'slug'],
// highlight-end
},
}
```
<Banner type="warning">
**Tip:**
If you are adding `listSearchableFields`, make sure you index each of these fields so your admin queries can remain performant.
</Banner>
## GraphQL

View File

@@ -6,7 +6,7 @@ desc: Set up your Global config for your needs by defining fields, adding slugs
keywords: globals, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
Globals are in many ways similar to [Collections](../configuration/collections), except that 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.
Globals are in many ways similar to [Collections](./collections), except that 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.
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.
@@ -25,7 +25,7 @@ export default buildConfig({
<Banner type="success">
**Tip:**
If you have more than one Global that share the same structure, consider using a [Collection](../configuration/collections) instead.
If you have more than one Global that share the same structure, consider using a [Collection](./collections) instead.
</Banner>
## Config Options
@@ -68,7 +68,7 @@ The following options are available:
| Option | Description |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`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). |
| **`admin`** | The configuration options for the Admin Panel. [More details](#admin-options). |
| **`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. |
@@ -80,7 +80,7 @@ The following options are available:
| **`lockDocuments`** | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |
| **`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). |
| **`versions`** | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#global-config). |
_* An asterisk denotes that a property is required._
@@ -96,9 +96,105 @@ Fields define the schema of the Global. To learn more, go to the [Fields](../fie
[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.
### Admin Options
## Admin Options
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.
The behavior of Globals within the [Admin Panel](../admin/overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](../admin/components), setting page metadata, and more.
To configure Admin Options for Globals, use the `admin` property in your Global Config:
```ts
import { GlobalConfig } from 'payload'
export const MyGlobal: GlobalConfig = {
// ...
admin: { // highlight-line
// ...
},
}
```
The following options are available:
| Option | Description |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **`group`** | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
| **`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](#custom-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](../admin/metadata). |
### Custom Components
Globals can set their own [Custom Components](../admin/components) which only apply to Global-specific UI within the [Admin Panel](../admin/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:
```ts
import type { SanitizedGlobalConfig } from 'payload'
export const MyGlobal: SanitizedGlobalConfig = {
// ...
admin: {
components: { // highlight-line
// ...
},
},
}
```
The following options are available:
| Path | Description |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| **`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](../admin/views). |
<Banner type="success">
**Note:**
For details on how to build Custom Components, see [Building Custom Components](../admin/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">
**Note:**
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>
## GraphQL

View File

@@ -24,7 +24,7 @@ The Payload Config is strongly typed and ties directly into Payload's TypeScript
<Banner type="success">
**Tip:**
The location of your Payload Config can be customized. [More details](#customizing--automating-config-location-detection).
The location of your Payload Config can be customized. [More details](#customizing-the-config-location).
</Banner>
## Config Options
@@ -73,7 +73,7 @@ The following options are available:
| **`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](#cross-origin-resource-sharing-cors). |
| **`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). |
| **`loggingLevels`** | An object to override the level to use in the logger for Payload's errors. |
@@ -108,7 +108,7 @@ _* An asterisk denotes that a property is required._
### 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.
Payload exposes a variety of TypeScript settings that you can leverage. These settings are used to auto-generate TypeScript interfaces for your [Collections](./collections) and [Globals](./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:
@@ -181,7 +181,7 @@ If none was in either location, Payload will finally check the `dist` directory.
### 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.
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:

View File

@@ -274,5 +274,5 @@ export default buildConfig({
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.
**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.
</Banner>

View File

@@ -52,7 +52,7 @@ export default buildConfig({
<Banner type="info">
**Note:**
If you're using `vercelPostgresAdapter` your `process.env.POSTGRES_URL` or `pool.connectionString` points to a local database (e.g hostname has `localhost` or `127.0.0.1`) we use the `pg` module for pooling instead of `@vercel/postgres`. This is because `@vercel/postgres` doesn't work with local databases, if you want to disable that behavior, you can pass `forceUseVercelPostgres: true` to adapter's 'args and follow [Vercel guide](https://vercel.com/docs/storage/vercel-postgres/local-development#option-2:-local-postgres-instance-with-docker) for a Docker Neon DB setup.
If you're using `vercelPostgresAdapter` your `process.env.POSTGRES_URL` or `pool.connectionString` points to a local database (e.g hostname has `localhost` or `127.0.0.1`) we use the `pg` module for pooling instead of `@vercel/postgres`. This is because `@vercel/postgres` doesn't work with local databases, if you want to disable that behavior, you can pass `forceUseVercelPostgres: true` to the adapter's args and follow [Vercel guide](https://vercel.com/docs/storage/vercel-postgres/local-development#option-2:-local-postgres-instance-with-docker) for a Docker Neon DB setup.
</Banner>
## Options

View File

@@ -79,12 +79,12 @@ export const MyArrayField: Field = {
}
```
The Array Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Array Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **`initCollapsed`** | Set the initial collapsed state |
| **`components.RowLabel`** | React component to be rendered as the label on the array row. [Example](#example-of-a-custom-rowlabel-component) |
| **`components.RowLabel`** | React component to be rendered as the label on the array row. [Example](#row-label) |
| **`isSortable`** | Disable order sorting by setting this value to `false` |
## Example
@@ -125,18 +125,99 @@ export const ExampleCollection: CollectionConfig = {
type: 'text',
},
],
admin: {
components: {
RowLabel: '/path/to/ArrayRowLabel#ArrayRowLabel',
},
},
},
],
}
```
### Example of a custom RowLabel component
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { ArrayField } from '@payloadcms/ui'
import type { ArrayFieldServerComponent } from 'payload'
export const CustomArrayFieldServer: ArrayFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions
}) => {
return (
<ArrayField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { ArrayField } from '@payloadcms/ui'
import type { ArrayFieldClientComponent } from 'payload'
export const CustomArrayFieldClient: ArrayFieldClientComponent = (props) => {
return <ArrayField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { ArrayFieldLabelServerComponent } from 'payload'
export const CustomArrayFieldLabelServer: ArrayFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import type { ArrayFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomArrayFieldLabelClient: ArrayFieldLabelClientComponent = ({
field,
path
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```
### Row Label
```tsx
'use client'

View File

@@ -77,7 +77,7 @@ export const MyBlocksField: Field = {
}
```
The Blocks Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Blocks Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ---------------------------------- |
@@ -209,6 +209,92 @@ export const ExampleCollection: CollectionConfig = {
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { BlocksField } from '@payloadcms/ui'
import type { BlocksFieldServerComponent } from 'payload'
export const CustomBlocksFieldServer: BlocksFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions
}) => {
return (
<BlocksField field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { BlocksField } from '@payloadcms/ui'
import type { BlocksFieldClientComponent } from 'payload'
export const CustomBlocksFieldClient: BlocksFieldClientComponent = (props) => {
return <BlocksField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { BlocksFieldLabelServerComponent } from 'payload'
export const CustomBlocksFieldLabelServer: BlocksFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { BlocksFieldLabelClientComponent } from 'payload'
export const CustomBlocksFieldLabelClient: BlocksFieldLabelClientComponent = ({
label,
path,
required,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```
## TypeScript
As you build your own Block configs, you might want to store them in separate files but retain typing accordingly. To do so, you can import and use Payload's `Block` type:

View File

@@ -41,7 +41,7 @@ export const MyCheckboxField: Field = {
| **`defaultValue`** | Provide data to be used for this field's default value, will default to false if field is also `required`. [More](/docs/fields/overview#default-values) |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](../admin/fields#admin-options). |
| **`admin`** | Admin-specific configuration. [More details](./overview#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
@@ -67,3 +67,90 @@ export const ExampleCollection: CollectionConfig = {
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { CheckboxField } from '@payloadcms/ui'
import type { CheckboxFieldServerComponent } from 'payload'
export const CustomCheckboxFieldServer: CheckboxFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<CheckboxField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { CheckboxField } from '@payloadcms/ui'
import type { CheckboxFieldClientComponent } from 'payload'
export const CustomCheckboxFieldClient: CheckboxFieldClientComponent = (props) => {
return <CheckboxField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { CheckboxFieldLabelServerComponent } from 'payload'
export const CustomCheckboxFieldLabelServer: CheckboxFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { CheckboxFieldLabelClientComponent } from 'payload'
export const CustomCheckboxFieldLabelClient: CheckboxFieldLabelClientComponent = ({
label,
path,
required,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -67,7 +67,7 @@ export const MyCodeField: Field = {
}
```
The Code Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Code Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -95,3 +95,85 @@ export const ExampleCollection: CollectionConfig = {
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { CodeField } from '@payloadcms/ui'
import type { CodeFieldServerComponent } from 'payload'
export const CustomCodeFieldServer: CodeFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<CodeField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { CodeField } from '@payloadcms/ui'
import type { CodeFieldClientComponent } from 'payload'
export const CustomCodeFieldClient: CodeFieldClientComponent = (props) => {
return <CodeField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { CodeFieldLabelServerComponent } from 'payload'
export const CustomCodeFieldLabelServer: CodeFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { CodeFieldLabelClientComponent } from 'payload'
export const CustomCodeFieldLabelClient: CodeFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -57,7 +57,7 @@ export const MyCollapsibleField: Field = {
}
```
The Collapsible Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Collapsible Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ------------------------------- |

View File

@@ -63,7 +63,7 @@ export const MyDateField: Field = {
}
```
The Date Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Date Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
@@ -135,3 +135,90 @@ export const ExampleCollection: CollectionConfig = {
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { DateTimeField } from '@payloadcms/ui'
import type { DateFieldServerComponent } from 'payload'
export const CustomDateFieldServer: DateFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<DateTimeField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { DateTimeField } from '@payloadcms/ui'
import type { DateFieldClientComponent } from 'payload'
export const CustomDateFieldClient: DateFieldClientComponent = (props) => {
return <DateTimeField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { DateFieldLabelServerComponent } from 'payload'
export const CustomDateFieldLabelServer: DateFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { DateFieldLabelClientComponent } from 'payload'
export const CustomDateFieldLabelClient: DateFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -64,7 +64,7 @@ export const MyEmailField: Field = {
}
```
The Email Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Email Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
@@ -90,3 +90,83 @@ export const ExampleCollection: CollectionConfig = {
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { EmailField } from '@payloadcms/ui'
import type { EmailFieldServerComponent } from 'payload'
export const CustomEmailFieldServer: EmailFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<EmailField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { EmailField } from '@payloadcms/ui'
import type { EmailFieldClientComponent } from 'payload'
export const CustomEmailFieldClient: EmailFieldClientComponent = (props) => {
return <EmailField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { EmailFieldLabelServerComponent } from 'payload'
export const CustomEmailFieldLabelServer: EmailFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { EmailFieldLabelClientComponent } from 'payload'
export const CustomEmailFieldLabelClient: EmailFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)}
```

View File

@@ -68,7 +68,7 @@ export const MyGroupField: Field = {
}
```
The Group Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Group Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -123,11 +123,11 @@ powerful Admin UI.
| Option | Description |
|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`name`** * | To be used as the property name when retrieved from the database. [More](/docs/fields/overview#field-names) |
| **`collection`** * | The `slug`s having the relationship field. |
| **`on`** * | The name of the relationship or upload field that relates to the collection document. Use dot notation for nested paths, like 'myGroup.relationName'. |
| **`name`** * | To be used as the property name when retrieved from the database. [More](./overview#field-names) |
| **`collection`** * | The `slug`s having the relationship field. |
| **`on`** * | The name of the relationship or upload field that relates to the collection document. Use dot notation for nested paths, like 'myGroup.relationName'. |
| **`where`** | A `Where` query to hide related documents from appearing. Will be merged with any `where` specified in the request. |
| **`maxDepth`** | Default is 1, Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](/docs/getting-started/concepts#field-level-max-depth). |
| **`maxDepth`** | Default is 1, Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](../queries/depth#max-depth). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
@@ -149,7 +149,7 @@ You can control the user experience of the join field using the `admin` config p
|------------------------|---------------------------------------------------------------------------------------------------------------------------|
| **`defaultColumns`** | Array of field names that correspond to which columns to show in the relationship table. Default is the collection config. |
| **`allowCreate`** | Set to `false` to remove the controls for making new related documents from this field. |
| **`components.Label`** | Override the default Label of the Field Component. [More details](../admin/fields#label) |
| **`components.Label`** | Override the default Label of the Field Component. [More details](./overview#label) |
## Join Field Data

View File

@@ -66,7 +66,7 @@ export const MyJSONField: Field = {
}
```
The JSON Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The JSON Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -154,3 +154,89 @@ export const ExampleCollection: CollectionConfig = {
// {"foo": "bar"} or {"foo": "foobar"} - ok
// Attempting to create {"foo": "not-bar"} will throw an error
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { JSONField } from '@payloadcms/ui'
import type { JSONFieldServerComponent } from 'payload'
export const CustomJSONFieldServer: JSONFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<JSONField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { JSONField } from '@payloadcms/ui'
import type { JSONFieldClientComponent } from 'payload'
export const CustomJSONFieldClient: JSONFieldClientComponent = (props) => {
return <JSONField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { JSONFieldLabelServerComponent } from 'payload'
export const CustomJSONFieldLabelServer: JSONFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { JSONFieldLabelClientComponent } from 'payload'
export const CustomJSONFieldLabelClient: JSONFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -69,7 +69,7 @@ export const MyNumberField: Field = {
}
```
The Number Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Number Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
@@ -98,3 +98,89 @@ export const ExampleCollection: CollectionConfig = {
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { NumberField } from '@payloadcms/ui'
import type { NumberFieldServerComponent } from 'payload'
export const CustomNumberFieldServer: NumberFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<NumberField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { NumberField } from '@payloadcms/ui'
import type { NumberFieldClientComponent } from 'payload'
export const CustomNumberFieldClient: NumberFieldClientComponent = (props) => {
return <NumberField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { NumberFieldLabelServerComponent } from 'payload'
export const CustomNumberFieldLabelServer: NumberFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { NumberFieldLabelClientComponent } from 'payload'
export const CustomNumberFieldLabelClient: NumberFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -8,7 +8,9 @@ keywords: overview, fields, config, configuration, documentation, Content Manage
Fields are the building blocks of Payload. They define the schema of the Documents that will be stored in the [Database](../database/overview), as well as automatically generate the corresponding UI within the [Admin Panel](../admin/overview).
There are many [Field Types](#field-types) to choose from, ranging anywhere from simple text strings to nested arrays and blocks. Most fields save data to the database, while others are strictly presentational. Fields can have [Custom Validations](#validation), [Conditional Logic](../admin/fields#conditional-logic), [Access Control](#field-level-access-control), [Hooks](#field-level-hooks), and so much more.
There are many [Field Types](#field-types) to choose from, ranging anywhere from simple text strings to nested arrays and blocks. Most fields save data to the database, while others are strictly presentational. Fields can have [Custom Validations](#validation), [Conditional Logic](./overview#conditional-logic), [Access Control](#field-level-access-control), [Hooks](#field-level-hooks), and so much more.
Fields 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](#custom-components).
To configure fields, use the `fields` property in your [Collection](../configuration/collections) or [Global](../configuration/globals) config:
@@ -23,10 +25,6 @@ export const Page: CollectionConfig = {
}
```
<Banner type="success">
You can fully customize the appearance and behavior of all fields within the Admin Panel. [More details](../admin/fields).
</Banner>
## Field Types
Payload provides a wide variety of built-in Field Types, each with its own unique properties and behaviors that determine which values it can accept, how it is presented in the API, and how it will be rendered in the [Admin Panel](../admin/overview).
@@ -54,10 +52,11 @@ export const Page: CollectionConfig = {
Each field is an object with at least the `type` property. This matches the field to its corresponding Field Type. [More details](#field-options).
</Banner>
There are two main categories of fields in Payload:
There are three main categories of fields in Payload:
- [Data Fields](#data-fields)
- [Presentational Fields](#presentational-fields)
- [Virtual Fields](#virtual-fields)
To begin writing fields, first determine which [Field Type](#field-types) best supports your application. Then to author your field accordingly using the [Field Options](#field-options) for your chosen field type.
@@ -93,14 +92,21 @@ Presentational Fields do not store data in the database. Instead, they are used
Here are the available Presentational Fields:
- [Collapsible](/docs/fields/collapsible) - nests fields within a collapsible component
- [Join](/docs/fields/join) - achieves two-way data binding between fields
- [Row](/docs/fields/row) - aligns fields horizontally
- [Tabs (Unnamed)](/docs/fields/tabs) - nests fields within a tabbed layout
- [UI](/docs/fields/ui) - blank field for custom UI components
<Banner type="warning">
### Virtual Fields
Virtual fields are used to display data that is not stored in the database. They are useful for displaying computed values that populate within the APi response through hooks, etc.
Here are the available Virtual Fields:
- [Join](/docs/fields/join) - achieves two-way data binding between fields
<Banner type="success">
**Tip:**
Don't see a Field Type that fits your needs? You can build your own using a [Custom Field Component](../admin/fields#field).
Don't see a built-in field type that you need? Build it! Using a combination of [Field Validations](#validation) and [Custom Components](../admin/components), you can override the entirety of how a component functions within the [Admin Panel](../admin/overview) to effectively create your own field type.
</Banner>
## Field Options
@@ -283,13 +289,14 @@ export const MyField: Field = {
The following additional properties are provided in the `ctx` object:
| Property | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `data` | An object containing the full collection or global document currently being edited. |
| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field. |
| `operation` | Will be `create` or `update` depending on the UI action or API call. |
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation. |
| `req` | The current HTTP request object. Contains `payload`, `user`, etc. |
| Property | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `data` | An object containing the full collection or global document currently being edited. |
| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field. |
| `operation` | Will be `create` or `update` depending on the UI action or API call. |
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation. |
| `req` | The current HTTP request object. Contains `payload`, `user`, etc. |
| `event` | Either `onChange` or `submit` depending on the current action. Used as a performance opt-in. [More details](#async-field-validations). |
#### Reusing Default Field Validations
@@ -310,10 +317,37 @@ const field: Field = {
}
```
Here is a list of all default field validation functions:
```ts
import {
array,
blocks,
checkbox,
code,
date,
email,
group,
json,
number,
point,
radio,
relationship,
richText,
select,
tabs,
text,
textarea,
upload,
} from 'payload/shared'
```
#### Async Field Validations
Custom validation functions can also be asynchronous depending on your needs. This makes it possible to make requests to external services or perform other miscellaneous asynchronous logic.
When writing async validation functions, it is important to consider the performance implications. Validations are executed on every change to the field, so they should be as lightweight as possible. If you need to perform expensive validations, such as querying the database, consider using the `event` property in the `ctx` object to only run the validation on form submission.
To write asynchronous validation functions, use the `async` keyword to define your function:
```ts
@@ -326,10 +360,18 @@ export const Orders: CollectionConfig = {
name: 'customerNumber',
type: 'text',
// highlight-start
validate: async (val, { operation }) => {
if (operation !== 'create') return true
validate: async (val, { event }) => {
if (event === 'onChange') {
return true
}
// only perform expensive validation when the form is submitted
const response = await fetch(`https://your-api.com/customers/${val}`)
if (response.ok) return true
if (response.ok) {
return true
}
return 'The customer number provided does not match any customers within our records.'
},
// highlight-end
@@ -338,24 +380,6 @@ export const Orders: CollectionConfig = {
}
```
### Admin Options
In addition to each field's base configuration, you can use the `admin` key to specify traits and properties for fields that will only effect how they are _rendered_ within the [Admin Panel](../admin/overview), such as their appearance or behavior.
```ts
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
admin: { // highlight-line
// ...
}
}
```
For full details on Admin Options, see the [Field Admin Options](../admin/fields) documentation.
## Custom ID Fields
All [Collections](../configuration/collections) automatically generate their own ID field. If needed, you can override this behavior by providing an explicit ID field to your config. This field should either be required or have a hook to generate the ID dynamically.
@@ -379,11 +403,503 @@ export const MyCollection: CollectionConfig = {
<Banner type="warning">
**Reminder:**
The Custom ID Fields can only be of type [`Number`](./number) or [`Text`](./text).
Custom ID fields with type `text` must not contain `/` or `.` characters.
The Custom ID Fields can only be of type [`Number`](./number) or [`Text`](./text). Custom ID fields with type `text` must not contain `/` or `.` characters.
</Banner>
## Admin Options
You can customize the appearance and behavior of fields within the [Admin Panel](../admin/overview) through the `admin` property of any Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: { // highlight-line
// ...
},
}
]
}
```
The following options are available:
| Option | Description |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`condition`** | Programmatically show / hide fields based on other fields. [More details](#conditional-logic). |
| **`components`** | All Field Components can be swapped out for [Custom Components](../admin/components) that you define. |
| **`description`** | Helper text to display alongside the field to provide more information for the editor. [More details](#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) entirely. |
| **`disableBulkEdit`** | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. Defaults to `true` for UI fields. |
| **`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 Descriptions
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](#description).
To add a Custom Description to a field, use the `admin.description` property in your Field Config:
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
description: 'Hello, world!' // highlight-line
},
},
]
}
```
<Banner type="warning">
**Reminder:**
To replace the Field Description with a [Custom Component](../admin/components), use the `admin.components.Description` property. [More details](#description).
</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 add a Description Function to a field, set the `admin.description` property to a _function_ in your Field Config:
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
description: ({ t }) => `${t('Hello, world!')}` // highlight-line
},
},
]
}
```
All Description Functions receive the following arguments:
| Argument | Description |
| -------------- | ---------------------------------------------------------------- |
| **`t`** | The `t` function used to internationalize the Admin Panel. [More details](../configuration/i18n) |
<Banner type="info">
**Note:**
If you need to subscribe to live updates within your form, use a Description Component instead. [More details](#description).
</Banner>
### 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.
**Example:**
```ts
{
fields: [
{
name: 'enableGreeting',
type: 'checkbox',
defaultValue: false,
},
{
name: 'greeting',
type: 'text',
admin: {
// highlight-start
condition: (data, siblingData, { user }) => {
if (data.enableGreeting) {
return true
} else {
return false
}
},
// highlight-end
},
},
]
}
```
### Custom Components
Within the [Admin Panel](../admin/overview), fields are represented in three distinct places:
- [Field](#field) - The actual form field rendered in the Edit View.
- [Cell](#cell) - The table cell component rendered in the List View.
- [Filter](#filter) - The filter component rendered in the List View.
To swap in Field Components with your own, use the `admin.components` property in your Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: { // highlight-line
// ...
},
},
}
]
}
```
The following options are available:
| Component | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------------------- |
| **`Field`** | The form field rendered of the Edit View. [More details](#field). |
| **`Cell`** | The table cell rendered of the List View. [More details](#cell). |
| **`Filter`** | The filter component rendered in the List View. [More details](#filter). |
| **`Label`** | Override the default Label of the Field Component. [More details](#label). |
| **`Error`** | Override the default Error of the Field Component. [More details](#error). |
| **`Description`** | Override the default Description of the Field Component. [More details](#description). |
| **`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). |
#### Field
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 swap in your own Field Component, use the `admin.components.Field` property in your Field Config:
```ts
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: {
Field: '/path/to/MyFieldComponent', // highlight-line
},
},
}
]
}
```
_For details on how to build Custom Components, see [Building Custom Components](../admin/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`](#label), [`Error`](#error), [`beforeInput`](#afterinput-and-beforinput), and [`afterInput`](#afterinput-and-beforinput) properties.
</Banner>
##### Default Props
All Field Components receive the following props by default:
| Property | Description |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`docPreferences`** | An object that contains the [Preferences](../admin/preferences) for the document. |
| **`field`** | In Client Components, this is the sanitized Client Field Config. In Server Components, this is the original Field Config. Server Components will also receive the sanitized field config through the`clientField` prop (see below). |
| **`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. |
| **`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, i.e. `posts.myGroup.myArray.myField`. |
| **`indexPath`** | A hyphen-notated string representing the path to the field _within the nearest named ancestor field_, i.e. `0-0` |
In addition to the above props, all Server Components will also receive the following props:
| Property | Description |
| ----------------- | ----------------------------------------------------------------------------- |
| **`clientField`** | The serializable Client Field Config. |
| **`field`** | The Field Config. |
| **`data`** | The current document being edited. |
| **`i18n`** | The [i18n](../configuration/i18n) object. |
| **`payload`** | The [Payload](../local-api/overview) class. |
| **`permissions`** | The field permissions based on the currently authenticated user. |
| **`siblingData`** | The data of the field's siblings. |
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
| **`value`** | The value of the field at render-time. |
##### 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`](../admin/hooks#usefield) hook from `@payloadcms/ui` and use it to manage the field's value:
```tsx
'use client'
import { useField } from '@payloadcms/ui'
export const CustomTextField: React.FC = () => {
const { value, setValue } = useField() // highlight-line
return (
<input
onChange={(e) => setValue(e.target.value)}
value={value}
/>
)
}
```
<Banner type="success">
For a complete list of all available React hooks, see the [Payload React Hooks](../admin/hooks) documentation. For additional help, see [Building Custom Components](../admin/components#building-custom-components).
</Banner>
##### TypeScript#field-component-types
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 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'
```
See each individual Field Type for exact type imports.
#### Cell
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 swap in your own Cell Component, use the `admin.components.Cell` property in your Field Config:
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Cell: '/path/to/MyCustomCellComponent', // highlight-line
},
},
}
```
All Cell Components receive the same [Default Field Component Props](#field), plus the following:
| Property | Description |
| ---------------- | ----------------------------------------------------------------- |
| **`link`** | A boolean representing whether this cell should be wrapped in a link. |
| **`onClick`** | A function that is called when the cell is clicked. |
For details on how to build Custom Components themselves, see [Building Custom Components](../admin/components#building-custom-components).
#### Filter
The Filter Component is the actual input element rendered within the "Filter By" dropdown of the List View used to represent this field when building filters.
To swap in your own Filter Component, use the `admin.components.Filter` property in your Field Config:
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Filter: '/path/to/MyCustomFilterComponent', // highlight-line
},
},
}
```
All Custom Filter Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](../admin/components#building-custom-components).
#### Label
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 swap in your own Label Component, use the `admin.components.Label` property in your Field Config:
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Label: '/path/to/MyCustomLabelComponent', // highlight-line
},
},
}
```
All Custom Label Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](../admin/components#building-custom-components).
##### TypeScript#label-component-types
When building Custom Label Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Label Component, one for every Field Type 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'
```
#### Description
Alternatively to the [Description Property](#field-descriptions), you can also use a [Custom Component](../admin/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 add a Description Component to a field, use the `admin.components.Description` property in your Field Config:
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
Description: '/path/to/MyCustomDescriptionComponent', // highlight-line
}
}
}
]
}
```
All Custom Description Components receive the same [Default Field Component Props](#field).
For details on how to build a Custom Components themselves, see [Building Custom Components](../admin/components#building-custom-components).
##### TypeScript#description-component-types
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 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'
```
#### Error
The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.
To swap in your own Error Component, use the `admin.components.Error` property in your Field Config:
```ts
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Error: '/path/to/MyCustomErrorComponent', // highlight-line
},
},
}
```
All Error Components receive the [Default Field Component Props](#field).
For details on how to build Custom Components themselves, see [Building Custom Components](../admin/components#building-custom-components).
##### TypeScript#error-component-types
When building Custom Error Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Error Component, one for every Field Type 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'
```
#### 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:
```ts
import type { SanitizedCollectionConfig } from 'payload'
export const MyCollectionConfig: SanitizedCollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
// highlight-start
beforeInput: ['/path/to/MyCustomComponent'],
afterInput: ['/path/to/MyOtherCustomComponent'],
// highlight-end
}
}
}
]
}
```
All `afterInput` and `beforeInput` Components receive the same [Default Field Component Props](#field).
For details on how to build Custom Components, see [Building Custom Components](../admin/components#building-custom-components).
## TypeScript
You can import the Payload `Field` type as well as other common types from the `payload` package. [More details](../typescript/overview).

View File

@@ -47,7 +47,7 @@ export const MyPointField: Field = {
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](../admin/fields#admin-options). |
| **`admin`** | Admin-specific configuration. [More details](./overview#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
@@ -129,3 +129,84 @@ payload.find({
},
})
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { PointField } from '@payloadcms/ui'
import type { PointFieldServerComponent } from 'payload'
export const CustomPointFieldServer: PointFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<PointField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { PointField } from '@payloadcms/ui'
import type { PointFieldClientComponent } from 'payload'
export const CustomPointFieldClient: PointFieldClientComponent = (props) => {
return <PointField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { PointFieldLabelServerComponent } from 'payload'
export const CustomPointFieldLabelServer: PointFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { PointFieldLabelClientComponent } from 'payload'
export const CustomPointFieldLabelClient: PointFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -79,7 +79,7 @@ export const MyRadioField: Field = {
}
```
The Radio Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Radio Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
@@ -117,3 +117,90 @@ export const ExampleCollection: CollectionConfig = {
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { RadioGroupField } from '@payloadcms/ui'
import type { RadioFieldServerComponent } from 'payload'
export const CustomRadioFieldServer: RadioFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<RadioGroupField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { RadioGroupField } from '@payloadcms/ui'
import type { RadioFieldClientComponent } from 'payload'
export const CustomRadioFieldClient: RadioFieldClientComponent = (props) => {
return <RadioGroupField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RadioFieldLabelServerComponent } from 'payload'
export const CustomRadioFieldLabelServer: RadioFieldLabelServerComponent = ({
clientField,
path,
required,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RadioFieldLabelClientComponent } from 'payload'
export const CustomRadioFieldLabelClient: RadioFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -85,7 +85,7 @@ export const MyRelationshipField: Field = {
}
```
The Relationship Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Relationship Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -373,3 +373,89 @@ Since we are referencing multiple collections, the field you are querying on may
You **cannot** query on a field within a polymorphic relationship as you would with a
non-polymorphic relationship.
</Banner>
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { RelationshipField } from '@payloadcms/ui'
import type { RelationshipFieldServerComponent } from 'payload'
export const CustomRelationshipFieldServer: RelationshipFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<RelationshipField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { RelationshipField } from '@payloadcms/ui'
import type { RelationshipFieldClientComponent } from 'payload'
export const CustomRelationshipFieldClient: RelationshipFieldClientComponent = (props) => {
return <RelationshipField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RelationshipFieldLabelServerComponent } from 'payload'
export const CustomRelationshipFieldLabelServer: RelationshipFieldLabelServerComponent = (
clientField,
path
) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RelationshipFieldLabelClientComponent } from 'payload'
export const CustomRelationshipFieldLabelClient: RelationshipFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -18,14 +18,14 @@ Instead, you can invest your time and effort into learning the underlying open-s
| Option | Description |
| --- | --- |
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |
| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](./overview#field-names) |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](./overview#validation) |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |
| **`defaultValue`** | Provide data to be used for this field's default value. [More](./overview#default-values) |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
@@ -38,7 +38,7 @@ Instead, you can invest your time and effort into learning the underlying open-s
## Admin Options
To customize the appearance and behavior of the Rich Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option. The Rich Text Field inherits all the default options from the base [Field Admin Config](../admin/fields#admin-options).
To customize the appearance and behavior of the Rich Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option. The Rich Text Field inherits all the default options from the base [Field Admin Config](./overview#admin-options).
```ts
import type { Field } from 'payload'
@@ -56,4 +56,4 @@ Further customization can be done with editor-specific options.
## Editor-specific Options
For a ton more editor-specific options, including how to build custom rich text elements directly into your editor,
take a look at the [rich text editor documentation](../rich-text/overview).
take a look at the [rich text editor documentation](../rich-text/overview).

View File

@@ -36,7 +36,7 @@ export const MyRowField: Field = {
| Option | Description |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`fields`** * | Array of field types to nest within this Row. |
| **`admin`** | Admin-specific configuration excluding `description`, `readOnly`, and `hidden`. [More details](../admin/fields#admin-options). |
| **`admin`** | Admin-specific configuration excluding `description`, `readOnly`, and `hidden`. [More details](./overview#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
_* An asterisk denotes that a property is required._

View File

@@ -81,7 +81,7 @@ export const MySelectField: Field = {
}
```
The Select Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Select Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Property | Description |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
@@ -125,83 +125,90 @@ export const ExampleCollection: CollectionConfig = {
}
```
## Customization
## Custom Components
The Select field UI component can be customized by providing a custom React component to the `components` object in the Base config.
### Field
```ts
export const CustomSelectField: Field = {
name: 'customSelectField',
type: 'select', // or 'text' if you have dynamic options
admin: {
components: {
Field: CustomSelectComponent({
options: [
{
label: 'Option 1',
value: '1',
},
{
label: 'Option 2',
value: '2',
},
],
}),
},
},
}
```
#### Server Component
You can import the existing Select component directly from Payload, then extend and customize it as needed.
```tsx
import type { SelectFieldServerComponent } from 'payload'
import type React from 'react'
```ts
import * as React from 'react';
import { SelectInput, useAuth, useField } from '@payloadcms/ui';
type CustomSelectProps = {
path: string;
options: {
label: string;
value: string;
}[];
}
export const CustomSelectComponent: React.FC<CustomSelectProps> = ({ path, options }) => {
const { value, setValue } = useField<string>({ path })
const { user } = useAuth()
const adjustedOptions = options.filter((option) => {
/*
A common use case for a custom select
is to show different options based on
the current user's role.
*/
return option;
});
import { SelectField } from '@payloadcms/ui'
export const CustomSelectFieldServer: SelectFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<div>
<label className="field-label">
Custom Select
</label>
<SelectInput
path={path}
name={path}
options={adjustedOptions}
value={value}
onChange={(e) => setValue(e.value)}
/>
</div>
<SelectField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
If you are looking to create a dynamic select field, the following tutorial will walk you through the process of creating a custom select field that fetches its options from an external API.
#### Client Component
<VideoDrawer
id="Efn9OxSjA6Y"
label="How to Create a Custom Select Field"
drawerTitle="How to Create a Custom Select Field: A Step-by-Step Guide"
/>
```tsx
'use client'
import type { SelectFieldClientComponent } from 'payload'
If you want to learn more about custom components check out the [Admin > Custom Component](/docs/admin/fields) docs.
import { SelectField } from '@payloadcms/ui'
import React from 'react'
export const CustomSelectFieldClient: SelectFieldClientComponent = (props) => {
return <SelectField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { SelectFieldLabelServerComponent } from 'payload'
export const CustomSelectFieldLabelServer: SelectFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { SelectFieldLabelClientComponent } from 'payload'
export const CustomSelectFieldLabelClient: SelectFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -36,7 +36,7 @@ export const MyTabsField: Field = {
| Option | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **`tabs`** * | Array of tabs to render within this Tabs field. |
| **`admin`** | Admin-specific configuration. [More details](../admin/fields#admin-options). |
| **`admin`** | Admin-specific configuration. [More details](./overview#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
### Tab-specific Config

View File

@@ -69,7 +69,7 @@ export const MyTextField: Field = {
}
```
The Text Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Text Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| -------------- | ---------------------------------------------------------------------------------------------------------------- |
@@ -95,3 +95,85 @@ export const ExampleCollection: CollectionConfig = {
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { TextField } from '@payloadcms/ui'
import type { TextFieldServerComponent } from 'payload'
export const CustomTextFieldServer: TextFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<TextField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { TextField } from '@payloadcms/ui'
import type { TextFieldClientComponent } from 'payload'
export const CustomTextFieldClient: TextFieldClientComponent = (props) => {
return <TextField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { TextFieldLabelServerComponent } from 'payload'
export const CustomTextFieldLabelServer: TextFieldLabelServerComponent = ({
clientField,
path,
required,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { TextFieldLabelClientComponent } from 'payload'
export const CustomTextFieldLabelClient: TextFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -66,7 +66,7 @@ export const MyTextareaField: Field = {
}
```
The Textarea Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:
The Textarea Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- |
@@ -93,3 +93,89 @@ export const ExampleCollection: CollectionConfig = {
],
}
```
## Custom Components
### Field
#### Server Component
```tsx
import type React from 'react'
import { TextareaField } from '@payloadcms/ui'
import type { TextareaFieldServerComponent } from 'payload'
export const CustomTextareaFieldServer: TextareaFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<TextareaField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { TextareaField } from '@payloadcms/ui'
import type { TextareaFieldClientComponent } from 'payload'
export const CustomTextareaFieldClient: TextareaFieldClientComponent = (props) => {
return <TextareaField {...props} />
}
```
### Label
#### Server Component
```tsx
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { TextareaFieldLabelServerComponent } from 'payload'
export const CustomTextareaFieldLabelServer: TextareaFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
```
#### Client Component
```tsx
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { TextareaFieldLabelClientComponent } from 'payload'
export const CustomTextareaFieldLabelClient: TextareaFieldLabelClientComponent = ({
field,
path,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
```

View File

@@ -32,8 +32,8 @@ export const MyUIField: Field = {
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **`name`** * | A unique identifier for this field. |
| **`label`** | Human-readable label for this UI field. |
| **`admin.components.Field`** * | React component to be rendered for this field within the Edit View. [More](../admin/fields#field) |
| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](../admin/fields#cell) |
| **`admin.components.Field`** * | React component to be rendered for this field within the Edit View. [More](./overview#field) |
| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](./overview#cell) |
| **`admin.disableListColumn`** | Set `disableListColumn` to `true` to prevent the UI field from appearing in the list view column selector. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |

View File

@@ -64,7 +64,7 @@ export const MyUploadField: Field = {
| **`displayPreview`** | Enable displaying preview of the uploaded file. Overrides related Collection's `displayPreview` option. [More](/docs/upload/overview#collection-upload-options). |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. [Admin Options](../admin/fields#admin-options). |
| **`admin`** | Admin-specific configuration. [Admin Options](./overview#admin-options). |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |

View File

@@ -50,7 +50,7 @@ Everything Payload does (create, read, update, delete, login, logout, etc.) is e
- [Local API](#local-api) - Extremely fast, direct-to-database access
- [REST API](#rest-api) - Standard HTTP endpoints for querying and mutating data
- [GraphQL](#graphql) - A full GraphQL API with a GraphQL Playground
- [GraphQL](#graphql-api) - A full GraphQL API with a GraphQL Playground
<Banner type="success">
**Note:**
@@ -157,7 +157,7 @@ All of Payload's GraphQL functionality is abstracted into a separate package. Pa
This is the UI library that Payload's Admin Panel uses. All components are exported from this package and can be re-used as you build extensions to the Payload admin UI, or want to use Payload components in your own React apps. Some exports are server components and some are client components.
`@payloadcms/db-postgres`, `@payloadcms/db-vercel-postgres`, `@payloadcms/db-mongodb`
`@payloadcms/db-postgres`, `@payloadcms/db-vercel-postgres`, `@payloadcms/db-mongodb`, `@payloadcms/db-sqlite`
You can choose which Database Adapter you'd like to use for your project, and no matter which you choose, the entire data layer for Payload is contained within these packages. You can only use one at a time for any given project.

View File

@@ -94,7 +94,7 @@ const Header: GlobalConfig = {
## Preferences
User [preferences](/docs/admin/overview#preferences) for the [Admin Panel](../admin/overview) are also available to GraphQL the same way as other collection schemas are generated. To query preferences you must supply an authorization token in the header and only the preferences of that user will be accessible.
User [preferences](../admin/preferences) for the [Admin Panel](../admin/overview) are also available to GraphQL the same way as other collection schemas are generated. To query preferences you must supply an authorization token in the header and only the preferences of that user will be accessible.
**Payload will open the following query:**

View File

@@ -27,7 +27,7 @@ All Field Hooks accept an array of synchronous or asynchronous functions. These
<Banner type="warning">
**Important:**
Due to GraphQL's typed nature, changing the type of data that you return from a field will produce errors in the [GraphQL API](../graphql/overview). If you need to change the shape or type of data, consider [Collection Hooks](./collections) or [Global Hooks](./hooks) instead.
Due to GraphQL's typed nature, changing the type of data that you return from a field will produce errors in the [GraphQL API](../graphql/overview). If you need to change the shape or type of data, consider [Collection Hooks](./collections) or [Global Hooks](./globals) instead.
</Banner>
To add hooks to a Field, use the `hooks` property in your [Field Config](../fields/overview):

View File

@@ -30,12 +30,12 @@ const config = buildConfig({
```
<Banner type="warning">
**Reminder:**
Alternatively, you can define the `admin.livePreview` property on individual [Collection Admin Configs](../admin/collections) and [Global Admin Configs](../admin/globals). Settings defined here will be merged into the top-level as overrides.
Alternatively, you can define the `admin.livePreview` property on individual [Collection Admin Configs](../configuration/collections#admin-options) and [Global Admin Configs](../configuration/globals#admin-options). Settings defined here will be merged into the top-level as overrides.
</Banner>
## Options
Setting up Live Preview is easy. This can be done either globally through the [Root Admin Config](../admin/overview), or on individual [Collection Admin Configs](../admin/collections) and [Global Admin Configs](../admin/globals). Once configured, a new "Live Preview" tab will appear at the top of enabled Documents. Navigating to this tab opens the preview window and loads your front-end application.
Setting up Live Preview is easy. This can be done either globally through the [Root Admin Config](../admin/overview), or on individual [Collection Admin Configs](../configuration/collections#admin-options) and [Global Admin Configs](../configuration/globals#admin-options). Once configured, a new "Live Preview" tab will appear at the top of enabled Documents. Navigating to this tab opens the preview window and loads your front-end application.
The following options are available:
@@ -104,8 +104,8 @@ The following arguments are provided to the `url` function:
| ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| **`data`** | The data of the Document being edited. This includes changes that have not yet been saved. |
| **`locale`** | The locale currently being edited (if applicable). [More details](../configuration/localization). |
| **`collectionConfig`** | The Collection Admin Config of the Document being edited. [More details](../admin/collections). |
| **`globalConfig`** | The Global Admin Config of the Document being edited. [More details](../admin/globals). |
| **`collectionConfig`** | The Collection Admin Config of the Document being edited. [More details](../configuration/collections#admin-options). |
| **`globalConfig`** | The Global Admin Config of the Document being edited. [More details](../configuration/globals#admin-options). |
| **`req`** | The Payload Request object. |
If your application requires a fully qualified URL, such as within deploying to Vercel Preview Deployments, you can use the `req` property to build this URL:

View File

@@ -19,7 +19,7 @@ Here are some common examples of how you can use the Local API:
- Fetching Payload data within React Server Components
- Seeding data via Node seed scripts that you write and maintain
- Opening custom Next.js route handlers which feature additional functionality but still rely on Payload
- Within [Access Control](../access-control) and [Hooks](../hooks/overview)
- Within [Access Control](../access-control/overview) and [Hooks](../hooks/overview)
## Accessing Payload

View File

@@ -1102,7 +1102,7 @@ plugins: [
## `@payloadcms/richtext-lexical`
If you have custom features for `@payloadcms/richtext-lexical` you will need to migrate your code to the new API. Read more about the new API in the [documentation](https://payloadcms.com/docs/lexical/building-custom-features).
If you have custom features for `@payloadcms/richtext-lexical` you will need to migrate your code to the new API. Read more about the new API in the [documentation](https://payloadcms.com/docs/rich-text/building-custom-features).
## Upgrade from previous beta

View File

@@ -6,7 +6,7 @@ desc: Scaffolds multi-tenancy for your Payload application
keywords: plugins, multi-tenant, multi-tenancy, plugin, payload, cms, seo, indexing, search, search engine
---
[![npm](https://img.shields.io/npm/v/@payloadcms/plugin-multi-tenants)](https://www.npmjs.com/package/@payloadcms/plugin-multi-tenants)
[![npm](https://img.shields.io/npm/v/@payloadcms/plugin-multi-tenant)](https://www.npmjs.com/package/@payloadcms/plugin-multi-tenant)
This plugin sets up multi-tenancy for your application from within your [Admin Panel](../admin/overview). It does so by adding a `tenant` field to all specified collections. Your front-end application can then query data by tenant. You must add the Tenants collection so you control what fields are available for each tenant.
@@ -31,7 +31,7 @@ This plugin sets up multi-tenancy for your application from within your [Admin P
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
```bash
pnpm add @payloadcms/plugin-multi-tenant@beta
pnpm add @payloadcms/plugin-multi-tenant
```
### Options

View File

@@ -39,23 +39,23 @@ _The exact query syntax will depend on the API you are using, but the concepts a
The following operators are available for use in queries:
| Operator | Description |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `equals` | The value must be exactly equal. |
| `not_equals` | The query will return all documents where the value is not equal. |
| `greater_than` | For numeric or date-based fields. |
| `greater_than_equal` | For numeric or date-based fields. |
| `less_than` | For numeric or date-based fields. |
| `less_than_equal` | For numeric or date-based fields. |
| `like` | Case-insensitive string must be present. If string of words, all words must be present, in any order. |
| `contains` | Must contain the value entered, case-insensitive. |
| `in` | The value must be found within the provided comma-delimited list of values. |
| `not_in` | The value must NOT be within the provided comma-delimited list of values. |
| `all` | The value must contain all values provided in the comma-delimited list. |
| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |
| `near` | For distance related to a [Point Field](../fields/point) comma separated as `<longitude>, <latitude>, <maxDistance in meters (nullable)>, <minDistance in meters (nullable)>`. |
| `within` | For [Point Fields](../fields/point) to filter documents based on whether points are inside of the given area defined in GeoJSON. [Example](../fields/point#querying-within) |
| `intersects` | For [Point Fields](../fields/point) to filter documents based on whether points intersect with the given area defined in GeoJSON. [Example](../fields/point#querying-intersects) |
| Operator | Description |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `equals` | The value must be exactly equal. |
| `not_equals` | The query will return all documents where the value is not equal. |
| `greater_than` | For numeric or date-based fields. |
| `greater_than_equal` | For numeric or date-based fields. |
| `less_than` | For numeric or date-based fields. |
| `less_than_equal` | For numeric or date-based fields. |
| `like` | Case-insensitive string must be present. If string of words, all words must be present, in any order. |
| `contains` | Must contain the value entered, case-insensitive. |
| `in` | The value must be found within the provided comma-delimited list of values. |
| `not_in` | The value must NOT be within the provided comma-delimited list of values. |
| `all` | The value must contain all values provided in the comma-delimited list. Note: currently this operator is supported only with the MongoDB adapter. |
| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |
| `near` | For distance related to a [Point Field](../fields/point) comma separated as `<longitude>, <latitude>, <maxDistance in meters (nullable)>, <minDistance in meters (nullable)>`. |
| `within` | For [Point Fields](../fields/point) to filter documents based on whether points are inside of the given area defined in GeoJSON. [Example](../fields/point#querying-within) |
| `intersects` | For [Point Fields](../fields/point) to filter documents based on whether points intersect with the given area defined in GeoJSON. [Example](../fields/point#querying-intersects) |
<Banner type="success">
**Tip:**

View File

@@ -138,7 +138,7 @@ export const Pages: CollectionConfig<'pages'> = {
```
<Banner type="warning">
<strong>Important:</strong>
**Important:**
When using `defaultPopulate` on a collection with [Uploads](/docs/fields/upload) enabled and you want to select the `url` field, it is important to specify `filename: true` as well, otherwise Payload will not be able to construct the correct file URL, instead returning `url: null`.
</Banner>

View File

@@ -20,6 +20,11 @@ All Payload API routes are mounted and prefixed to your config's `routes.api` UR
- [fallback-locale](/docs/configuration/localization#retrieving-localized-docs) - specifies a fallback locale if no locale value exists
- [select](../queries/select) - specifies which fields to include to the result
- [populate](../queries/select#populate) - specifies which fields to include to the result from populated documents
- [limit](../queries/pagination#pagination-controls) - limits the number of documents returned
- [page](../queries/pagination#pagination-controls) - specifies which page to get documents from when used with a limit
- [sort](../queries/sort#rest-api) - specifies the field(s) to use to sort the returned documents by
- [where](../queries/overview) - specifies advanced filters to use to query documents
- [joins](/docs/fields/join#rest-api) - specifies the custom request for each join field by name of the field
## Collections
@@ -658,7 +663,7 @@ Data is not automatically appended to the request. You can read the body data by
Or you could use our helper function that mutates the request and appends data and file if found.
```ts
import { addDataAndFileToRequest } from '@payloadcms/next/utilities'
import { addDataAndFileToRequest } from 'payload'
// custom endpoint example
{
@@ -684,7 +689,7 @@ import { addDataAndFileToRequest } from '@payloadcms/next/utilities'
The locale and the fallback locale are not automatically appended to custom endpoint requests. If you would like to add them you can use this helper function.
```ts
import { addLocalesToRequestFromData } from '@payloadcms/next/utilities'
import { addLocalesToRequestFromData } from 'payload'
// custom endpoint example
{

View File

@@ -1,8 +1,8 @@
---
title: Lexical Building Custom Features
title: Custom Features
label: Custom Features
order: 40
desc: Building custom lexical features
desc: Building custom features
keywords: lexical, rich text, editor, headless cms, feature, features
---

View File

@@ -7,21 +7,21 @@ title: Rich Text Editor
---
<Banner type="success">
The Payload editor is based on Lexical, Meta's rich text editor. The previous default editor was based on Slate and is still supported. You can read [its documentation](../rich-text/slate), or the optional [migration guide](../rich-text/migration) to migrate from Slate to Lexical (recommended).
This documentation is about our new editor, based on Lexical (Meta's rich text editor). The previous default
editor was based on Slate and is still supported. You can read [its documentation](/docs/rich-text/slate),
or the optional [migration guide](/docs/rich-text/migration) to migrate from Slate to Lexical (recommended).
</Banner>
One of Payload's goals is to build the best rich text editor experience that we possibly can. We want to combine the beauty and polish of the Medium editing experience with the strength and features of the Notion editor - all in one place.
The editor is the most important property of the [rich text field](/docs/fields/rich-text).
Classically, we've used SlateJS to work toward this goal, but building custom elements into Slate has proven to be more difficult than we'd like, and we've been keeping our options open.
As a key part of Payload, we are proud to offer you the best editing experience you can imagine. With healthy
defaults out of the box, but also with the flexibility to customize every detail: from the “/” menu
and toolbars (whether inline or fixed) to inserting any component or subfield you can imagine.
Lexical is extremely impressive and trivializes a lot of the hard parts of building new elements into a rich text editor. It has a few distinct advantages over Slate, including the following:
1. A "/" menu, which allows editors to easily add new elements while never leaving their keyboard
2. A "hover" toolbar that pops up if you select text
3. It supports Payload blocks natively, directly within your rich text editor
4. Custom elements, called "features", are much easier to build in Lexical vs. Slate
To use the Lexical editor, first you need to install it:
To use the rich text editor, first you need to install it:
```bash
pnpm install @payloadcms/richtext-lexical

View File

@@ -6,9 +6,13 @@ desc: The Slate editor has been supported by Payload since beta. It's very power
keywords: slatejs, slate, rich text, editor, headless cms
---
The Slate editor has been supported by Payload since we released our initial beta. It's battle-tested and will continue to be supported into the future.
<Banner type="warning">
If you are migrating a Payload project from 1.x or earlier, you can continue to use the Slate editor as long as you'd like.
The [default Payload editor](/docs/rich-text/overview) is currently based on Lexical. This documentation
is about our old Slate-based editor. You can continue using it because it is still supported, or you can
see the optional [migration guide](/docs/rich-text/migration) to migrate from Slate to Lexical (recommended).
</Banner>
To use the Slate editor, first you need to install it:

View File

@@ -21,6 +21,7 @@ export const defaultESLintIgnores = [
'**/temp/',
'**/*.spec.ts',
'next-env.d.ts',
'**/app',
]
/** @typedef {import('eslint').Linter.Config} Config */

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,10 @@ import { CustomCheckboxFieldLabelServer as CustomCheckboxFieldLabelServer_48cd2d
import { CustomCheckboxFieldServer as CustomCheckboxFieldServer_85023d60242dd4cca7c406a728ec37a8 } from '@/collections/Fields/checkbox/components/server/Field'
import { CustomCheckboxFieldLabelClient as CustomCheckboxFieldLabelClient_f2b214145c1cbe98957573cf62455194 } from '@/collections/Fields/checkbox/components/client/Label'
import { CustomCheckboxFieldClient as CustomCheckboxFieldClient_a13e6003bc89da826df764d7234782de } from '@/collections/Fields/checkbox/components/client/Field'
import { CustomCodeFieldLabelServer as CustomCodeFieldLabelServer_566aaa8491895af5900235f95e62c2cf } from '@/collections/Fields/code/components/server/Label'
import { CustomCodeFieldServer as CustomCodeFieldServer_e76f292770f788ad4ec46a180ab3c174 } from '@/collections/Fields/code/components/server/Field'
import { CustomCodeFieldLabelClient as CustomCodeFieldLabelClient_823b02f23d594204cfa7fcfc30aeed46 } from '@/collections/Fields/code/components/client/Label'
import { CustomCodeFieldClient as CustomCodeFieldClient_2ef913b825e7bb326ec86b0f6ea4dd3c } from '@/collections/Fields/code/components/client/Field'
import { CustomDateFieldLabelServer as CustomDateFieldLabelServer_ae9eb459b79a1363a40d62b1e463aa6b } from '@/collections/Fields/date/components/server/Label'
import { CustomDateFieldServer as CustomDateFieldServer_9d448604d99b3b06826ea95986ffd27b } from '@/collections/Fields/date/components/server/Field'
import { CustomDateFieldLabelClient as CustomDateFieldLabelClient_0b6c1439c63aadfd306cf432713a52d8 } from '@/collections/Fields/date/components/client/Label'
@@ -16,6 +20,10 @@ import { CustomEmailFieldLabelServer as CustomEmailFieldLabelServer_a5097abb06ef
import { CustomEmailFieldServer as CustomEmailFieldServer_457ae84519701bf0b287c30a994ddab1 } from '@/collections/Fields/email/components/server/Field'
import { CustomEmailFieldLabelClient as CustomEmailFieldLabelClient_147edabdb378c855b9c8c06b8c627e50 } from '@/collections/Fields/email/components/client/Label'
import { CustomEmailFieldClient as CustomEmailFieldClient_e22bcec891915f255e5ac6e1850aeb97 } from '@/collections/Fields/email/components/client/Field'
import { CustomJSONFieldLabelServer as CustomJSONFieldLabelServer_94d8c8f7c699cb282fb912bead88f8b6 } from '@/collections/Fields/json/components/server/Label'
import { CustomJSONFieldServer as CustomJSONFieldServer_6034ebd8b071080c9e06e4abab7a6fd2 } from '@/collections/Fields/json/components/server/Field'
import { CustomJSONFieldLabelClient as CustomJSONFieldLabelClient_16798ccfd3eb2cb009c4f0ea34c3c31e } from '@/collections/Fields/json/components/client/Label'
import { CustomJSONFieldClient as CustomJSONFieldClient_077b2498f1ebe1df34940ffc4ce4f8e6 } from '@/collections/Fields/json/components/client/Field'
import { CustomNumberFieldLabelServer as CustomNumberFieldLabelServer_1b47d0cd70ad88e23dcf9c4f91bf319f } from '@/collections/Fields/number/components/server/Label'
import { CustomNumberFieldServer as CustomNumberFieldServer_54fc6ad95d89a3b66b59136e84d20b86 } from '@/collections/Fields/number/components/server/Field'
import { CustomNumberFieldLabelClient as CustomNumberFieldLabelClient_dd3e3dcfc7b07c3a02f947ac81718a51 } from '@/collections/Fields/number/components/client/Label'
@@ -55,59 +63,130 @@ import { CustomDefaultRootView as CustomDefaultRootView_a2f8ce99b3a1692f7ec03a90
import { CustomMinimalRootView as CustomMinimalRootView_9211f699dea5524a957f33011b786586 } from '@/components/views/CustomMinimalRootView'
export const importMap = {
"@/collections/Fields/array/components/server/Label#CustomArrayFieldLabelServer": CustomArrayFieldLabelServer_f8d063e9b7f25c350451c1865199c947,
"@/collections/Fields/array/components/server/Field#CustomArrayFieldServer": CustomArrayFieldServer_4c3c139a9b1a198103c8a2ec2869c837,
"@/collections/Fields/array/components/client/Label#CustomArrayFieldLabelClient": CustomArrayFieldLabelClient_c07dc2c547c47aca8e9f471795279e9d,
"@/collections/Fields/array/components/client/Field#CustomArrayFieldClient": CustomArrayFieldClient_60ede271f2b85983daf36710010ad8ab,
"@/collections/Fields/blocks/components/server/Field#CustomBlocksFieldServer": CustomBlocksFieldServer_61732537ad2c492ac9938959902f6954,
"@/collections/Fields/blocks/components/client/Field#CustomBlocksFieldClient": CustomBlocksFieldClient_2ef3a03de3974b6f18f07623af0cd515,
"@/collections/Fields/checkbox/components/server/Label#CustomCheckboxFieldLabelServer": CustomCheckboxFieldLabelServer_48cd2d9639f54745ad4cdb6905c825d9,
"@/collections/Fields/checkbox/components/server/Field#CustomCheckboxFieldServer": CustomCheckboxFieldServer_85023d60242dd4cca7c406a728ec37a8,
"@/collections/Fields/checkbox/components/client/Label#CustomCheckboxFieldLabelClient": CustomCheckboxFieldLabelClient_f2b214145c1cbe98957573cf62455194,
"@/collections/Fields/checkbox/components/client/Field#CustomCheckboxFieldClient": CustomCheckboxFieldClient_a13e6003bc89da826df764d7234782de,
"@/collections/Fields/date/components/server/Label#CustomDateFieldLabelServer": CustomDateFieldLabelServer_ae9eb459b79a1363a40d62b1e463aa6b,
"@/collections/Fields/date/components/server/Field#CustomDateFieldServer": CustomDateFieldServer_9d448604d99b3b06826ea95986ffd27b,
"@/collections/Fields/date/components/client/Label#CustomDateFieldLabelClient": CustomDateFieldLabelClient_0b6c1439c63aadfd306cf432713a52d8,
"@/collections/Fields/date/components/client/Field#CustomDateFieldClient": CustomDateFieldClient_4ef537c727f5de7c26aaea94024a0b2c,
"@/collections/Fields/email/components/server/Label#CustomEmailFieldLabelServer": CustomEmailFieldLabelServer_a5097abb06efbe71fc6ba1636f7194ab,
"@/collections/Fields/email/components/server/Field#CustomEmailFieldServer": CustomEmailFieldServer_457ae84519701bf0b287c30a994ddab1,
"@/collections/Fields/email/components/client/Label#CustomEmailFieldLabelClient": CustomEmailFieldLabelClient_147edabdb378c855b9c8c06b8c627e50,
"@/collections/Fields/email/components/client/Field#CustomEmailFieldClient": CustomEmailFieldClient_e22bcec891915f255e5ac6e1850aeb97,
"@/collections/Fields/number/components/server/Label#CustomNumberFieldLabelServer": CustomNumberFieldLabelServer_1b47d0cd70ad88e23dcf9c4f91bf319f,
"@/collections/Fields/number/components/server/Field#CustomNumberFieldServer": CustomNumberFieldServer_54fc6ad95d89a3b66b59136e84d20b86,
"@/collections/Fields/number/components/client/Label#CustomNumberFieldLabelClient": CustomNumberFieldLabelClient_dd3e3dcfc7b07c3a02f947ac81718a51,
"@/collections/Fields/number/components/client/Field#CustomNumberFieldClient": CustomNumberFieldClient_5d5605680426c77470fd74d010fe051f,
"@/collections/Fields/point/components/server/Label#CustomPointFieldLabelServer": CustomPointFieldLabelServer_c5fb0c717f353a8c6149238dd7d92ec9,
"@/collections/Fields/point/components/server/Field#CustomPointFieldServer": CustomPointFieldServer_a23d13971ed0ff10615e3248bb1ee55d,
"@/collections/Fields/point/components/client/Label#CustomPointFieldLabelClient": CustomPointFieldLabelClient_3c6c8c891bc098021e618d5cf4dc3150,
"@/collections/Fields/point/components/client/Field#CustomPointFieldClient": CustomPointFieldClient_abb4ee1633cbc83b4cec9b8abb95f132,
"@/collections/Fields/radio/components/server/Label#CustomRadioFieldLabelServer": CustomRadioFieldLabelServer_5c732ac2af72bb41657cc9a1a22bc67b,
"@/collections/Fields/radio/components/server/Field#CustomRadioFieldServer": CustomRadioFieldServer_b7edb363e225e2976a994da8e8803e60,
"@/collections/Fields/radio/components/client/Label#CustomRadioFieldLabelClient": CustomRadioFieldLabelClient_d46d0583023d87065f05972901727bbf,
"@/collections/Fields/radio/components/client/Field#CustomRadioFieldClient": CustomRadioFieldClient_42845db96f999817cb9f0a590413d669,
"@/collections/Fields/relationship/components/server/Label#CustomRelationshipFieldLabelServer": CustomRelationshipFieldLabelServer_7c45510caabe204587b638c40f0d0a70,
"@/collections/Fields/relationship/components/server/Field#CustomRelationshipFieldServer": CustomRelationshipFieldServer_d2e0b17d4b1c00b1fc726f0ea55ddc16,
"@/collections/Fields/relationship/components/client/Label#CustomRelationshipFieldLabelClient": CustomRelationshipFieldLabelClient_37b268226ded7dd38d5cb8f2952f4b3a,
"@/collections/Fields/relationship/components/client/Field#CustomRelationshipFieldClient": CustomRelationshipFieldClient_eb1bc838beb92b05ba1bb9c1fdfd7869,
"@/collections/Fields/select/components/server/Label#CustomSelectFieldLabelServer": CustomSelectFieldLabelServer_653acab80b672fd4ebeeed757e09d4c9,
"@/collections/Fields/select/components/server/Field#CustomSelectFieldServer": CustomSelectFieldServer_ee886c859ef756c29ae7383a2be0a08a,
"@/collections/Fields/select/components/client/Label#CustomSelectFieldLabelClient": CustomSelectFieldLabelClient_2db542ef2e0a664acaa5679fc14aa54b,
"@/collections/Fields/select/components/client/Field#CustomSelectFieldClient": CustomSelectFieldClient_c8b4c7f3e98b5887ca262dd841bffa2f,
"@/collections/Fields/text/components/server/Label#CustomTextFieldLabelServer": CustomTextFieldLabelServer_64a4b68861269d69d4c16a0f651b7ac9,
"@/collections/Fields/text/components/server/Field#CustomTextFieldServer": CustomTextFieldServer_e0caaef49c00003336b08d834c0c9fe9,
"@/collections/Fields/text/components/client/Label#CustomTextFieldLabelClient": CustomTextFieldLabelClient_9af2b9e4733a9fc79fb9dfb1578c18bf,
"@/collections/Fields/text/components/client/Field#CustomTextFieldClient": CustomTextFieldClient_c7c0687b5204b201f8b1af831f34fd98,
"@/collections/Fields/textarea/components/server/Label#CustomTextareaFieldLabelServer": CustomTextareaFieldLabelServer_5c8f706a3452bccefa9f5044e2cd250c,
"@/collections/Fields/textarea/components/server/Field#CustomTextareaFieldServer": CustomTextareaFieldServer_3f7b621f5c4c42971fc099a1fa492d99,
"@/collections/Fields/textarea/components/client/Label#CustomTextareaFieldLabelClient": CustomTextareaFieldLabelClient_9959ee64353edb5f2606b52187275823,
"@/collections/Fields/textarea/components/client/Field#CustomTextareaFieldClient": CustomTextareaFieldClient_4fd3331c38982e86768c64dcc9a10691,
"@/collections/Views/components/CustomTabEditView#CustomTabEditView": CustomTabEditView_0a7acb05a3192ecfa7e07f8b42e7a193,
"@/collections/Views/components/CustomDefaultEditView#CustomDefaultEditView": CustomDefaultEditView_2d3c652c5909d3a3dc3464f0547d5424,
"@/collections/RootViews/components/CustomRootEditView#CustomRootEditView": CustomRootEditView_ba37229da543ad3c8dc40f7a48771f99,
"@/components/afterNavLinks/LinkToCustomView#LinkToCustomView": LinkToCustomView_6f16fe358985478a2ead2354ef2cc9a0,
"@/components/afterNavLinks/LinkToCustomMinimalView#LinkToCustomMinimalView": LinkToCustomMinimalView_fd2cefb054695a5b60b860a69d67d15d,
"@/components/afterNavLinks/LinkToCustomDefaultView#LinkToCustomDefaultView": LinkToCustomDefaultView_4c5f581c8bfa951ce2f83c24c4f36b3b,
"@/components/views/CustomRootView#CustomRootView": CustomRootView_1ebb91ef5ff1ea4dc9a27ceb8e9ee0ab,
"@/components/views/CustomDefaultRootView#CustomDefaultRootView": CustomDefaultRootView_a2f8ce99b3a1692f7ec03a907e1ea4ce,
"@/components/views/CustomMinimalRootView#CustomMinimalRootView": CustomMinimalRootView_9211f699dea5524a957f33011b786586
'@/collections/Fields/array/components/server/Label#CustomArrayFieldLabelServer':
CustomArrayFieldLabelServer_f8d063e9b7f25c350451c1865199c947,
'@/collections/Fields/array/components/server/Field#CustomArrayFieldServer':
CustomArrayFieldServer_4c3c139a9b1a198103c8a2ec2869c837,
'@/collections/Fields/array/components/client/Label#CustomArrayFieldLabelClient':
CustomArrayFieldLabelClient_c07dc2c547c47aca8e9f471795279e9d,
'@/collections/Fields/array/components/client/Field#CustomArrayFieldClient':
CustomArrayFieldClient_60ede271f2b85983daf36710010ad8ab,
'@/collections/Fields/blocks/components/server/Field#CustomBlocksFieldServer':
CustomBlocksFieldServer_61732537ad2c492ac9938959902f6954,
'@/collections/Fields/blocks/components/client/Field#CustomBlocksFieldClient':
CustomBlocksFieldClient_2ef3a03de3974b6f18f07623af0cd515,
'@/collections/Fields/checkbox/components/server/Label#CustomCheckboxFieldLabelServer':
CustomCheckboxFieldLabelServer_48cd2d9639f54745ad4cdb6905c825d9,
'@/collections/Fields/checkbox/components/server/Field#CustomCheckboxFieldServer':
CustomCheckboxFieldServer_85023d60242dd4cca7c406a728ec37a8,
'@/collections/Fields/checkbox/components/client/Label#CustomCheckboxFieldLabelClient':
CustomCheckboxFieldLabelClient_f2b214145c1cbe98957573cf62455194,
'@/collections/Fields/checkbox/components/client/Field#CustomCheckboxFieldClient':
CustomCheckboxFieldClient_a13e6003bc89da826df764d7234782de,
'@/collections/Fields/code/components/server/Label#CustomCodeFieldLabelServer':
CustomCodeFieldLabelServer_566aaa8491895af5900235f95e62c2cf,
'@/collections/Fields/code/components/server/Field#CustomCodeFieldServer':
CustomCodeFieldServer_e76f292770f788ad4ec46a180ab3c174,
'@/collections/Fields/code/components/client/Label#CustomCodeFieldLabelClient':
CustomCodeFieldLabelClient_823b02f23d594204cfa7fcfc30aeed46,
'@/collections/Fields/code/components/client/Field#CustomCodeFieldClient':
CustomCodeFieldClient_2ef913b825e7bb326ec86b0f6ea4dd3c,
'@/collections/Fields/date/components/server/Label#CustomDateFieldLabelServer':
CustomDateFieldLabelServer_ae9eb459b79a1363a40d62b1e463aa6b,
'@/collections/Fields/date/components/server/Field#CustomDateFieldServer':
CustomDateFieldServer_9d448604d99b3b06826ea95986ffd27b,
'@/collections/Fields/date/components/client/Label#CustomDateFieldLabelClient':
CustomDateFieldLabelClient_0b6c1439c63aadfd306cf432713a52d8,
'@/collections/Fields/date/components/client/Field#CustomDateFieldClient':
CustomDateFieldClient_4ef537c727f5de7c26aaea94024a0b2c,
'@/collections/Fields/email/components/server/Label#CustomEmailFieldLabelServer':
CustomEmailFieldLabelServer_a5097abb06efbe71fc6ba1636f7194ab,
'@/collections/Fields/email/components/server/Field#CustomEmailFieldServer':
CustomEmailFieldServer_457ae84519701bf0b287c30a994ddab1,
'@/collections/Fields/email/components/client/Label#CustomEmailFieldLabelClient':
CustomEmailFieldLabelClient_147edabdb378c855b9c8c06b8c627e50,
'@/collections/Fields/email/components/client/Field#CustomEmailFieldClient':
CustomEmailFieldClient_e22bcec891915f255e5ac6e1850aeb97,
'@/collections/Fields/json/components/server/Label#CustomJSONFieldLabelServer':
CustomJSONFieldLabelServer_94d8c8f7c699cb282fb912bead88f8b6,
'@/collections/Fields/json/components/server/Field#CustomJSONFieldServer':
CustomJSONFieldServer_6034ebd8b071080c9e06e4abab7a6fd2,
'@/collections/Fields/json/components/client/Label#CustomJSONFieldLabelClient':
CustomJSONFieldLabelClient_16798ccfd3eb2cb009c4f0ea34c3c31e,
'@/collections/Fields/json/components/client/Field#CustomJSONFieldClient':
CustomJSONFieldClient_077b2498f1ebe1df34940ffc4ce4f8e6,
'@/collections/Fields/number/components/server/Label#CustomNumberFieldLabelServer':
CustomNumberFieldLabelServer_1b47d0cd70ad88e23dcf9c4f91bf319f,
'@/collections/Fields/number/components/server/Field#CustomNumberFieldServer':
CustomNumberFieldServer_54fc6ad95d89a3b66b59136e84d20b86,
'@/collections/Fields/number/components/client/Label#CustomNumberFieldLabelClient':
CustomNumberFieldLabelClient_dd3e3dcfc7b07c3a02f947ac81718a51,
'@/collections/Fields/number/components/client/Field#CustomNumberFieldClient':
CustomNumberFieldClient_5d5605680426c77470fd74d010fe051f,
'@/collections/Fields/point/components/server/Label#CustomPointFieldLabelServer':
CustomPointFieldLabelServer_c5fb0c717f353a8c6149238dd7d92ec9,
'@/collections/Fields/point/components/server/Field#CustomPointFieldServer':
CustomPointFieldServer_a23d13971ed0ff10615e3248bb1ee55d,
'@/collections/Fields/point/components/client/Label#CustomPointFieldLabelClient':
CustomPointFieldLabelClient_3c6c8c891bc098021e618d5cf4dc3150,
'@/collections/Fields/point/components/client/Field#CustomPointFieldClient':
CustomPointFieldClient_abb4ee1633cbc83b4cec9b8abb95f132,
'@/collections/Fields/radio/components/server/Label#CustomRadioFieldLabelServer':
CustomRadioFieldLabelServer_5c732ac2af72bb41657cc9a1a22bc67b,
'@/collections/Fields/radio/components/server/Field#CustomRadioFieldServer':
CustomRadioFieldServer_b7edb363e225e2976a994da8e8803e60,
'@/collections/Fields/radio/components/client/Label#CustomRadioFieldLabelClient':
CustomRadioFieldLabelClient_d46d0583023d87065f05972901727bbf,
'@/collections/Fields/radio/components/client/Field#CustomRadioFieldClient':
CustomRadioFieldClient_42845db96f999817cb9f0a590413d669,
'@/collections/Fields/relationship/components/server/Label#CustomRelationshipFieldLabelServer':
CustomRelationshipFieldLabelServer_7c45510caabe204587b638c40f0d0a70,
'@/collections/Fields/relationship/components/server/Field#CustomRelationshipFieldServer':
CustomRelationshipFieldServer_d2e0b17d4b1c00b1fc726f0ea55ddc16,
'@/collections/Fields/relationship/components/client/Label#CustomRelationshipFieldLabelClient':
CustomRelationshipFieldLabelClient_37b268226ded7dd38d5cb8f2952f4b3a,
'@/collections/Fields/relationship/components/client/Field#CustomRelationshipFieldClient':
CustomRelationshipFieldClient_eb1bc838beb92b05ba1bb9c1fdfd7869,
'@/collections/Fields/select/components/server/Label#CustomSelectFieldLabelServer':
CustomSelectFieldLabelServer_653acab80b672fd4ebeeed757e09d4c9,
'@/collections/Fields/select/components/server/Field#CustomSelectFieldServer':
CustomSelectFieldServer_ee886c859ef756c29ae7383a2be0a08a,
'@/collections/Fields/select/components/client/Label#CustomSelectFieldLabelClient':
CustomSelectFieldLabelClient_2db542ef2e0a664acaa5679fc14aa54b,
'@/collections/Fields/select/components/client/Field#CustomSelectFieldClient':
CustomSelectFieldClient_c8b4c7f3e98b5887ca262dd841bffa2f,
'@/collections/Fields/text/components/server/Label#CustomTextFieldLabelServer':
CustomTextFieldLabelServer_64a4b68861269d69d4c16a0f651b7ac9,
'@/collections/Fields/text/components/server/Field#CustomTextFieldServer':
CustomTextFieldServer_e0caaef49c00003336b08d834c0c9fe9,
'@/collections/Fields/text/components/client/Label#CustomTextFieldLabelClient':
CustomTextFieldLabelClient_9af2b9e4733a9fc79fb9dfb1578c18bf,
'@/collections/Fields/text/components/client/Field#CustomTextFieldClient':
CustomTextFieldClient_c7c0687b5204b201f8b1af831f34fd98,
'@/collections/Fields/textarea/components/server/Label#CustomTextareaFieldLabelServer':
CustomTextareaFieldLabelServer_5c8f706a3452bccefa9f5044e2cd250c,
'@/collections/Fields/textarea/components/server/Field#CustomTextareaFieldServer':
CustomTextareaFieldServer_3f7b621f5c4c42971fc099a1fa492d99,
'@/collections/Fields/textarea/components/client/Label#CustomTextareaFieldLabelClient':
CustomTextareaFieldLabelClient_9959ee64353edb5f2606b52187275823,
'@/collections/Fields/textarea/components/client/Field#CustomTextareaFieldClient':
CustomTextareaFieldClient_4fd3331c38982e86768c64dcc9a10691,
'@/collections/Views/components/CustomTabEditView#CustomTabEditView':
CustomTabEditView_0a7acb05a3192ecfa7e07f8b42e7a193,
'@/collections/Views/components/CustomDefaultEditView#CustomDefaultEditView':
CustomDefaultEditView_2d3c652c5909d3a3dc3464f0547d5424,
'@/collections/RootViews/components/CustomRootEditView#CustomRootEditView':
CustomRootEditView_ba37229da543ad3c8dc40f7a48771f99,
'@/components/afterNavLinks/LinkToCustomView#LinkToCustomView':
LinkToCustomView_6f16fe358985478a2ead2354ef2cc9a0,
'@/components/afterNavLinks/LinkToCustomMinimalView#LinkToCustomMinimalView':
LinkToCustomMinimalView_fd2cefb054695a5b60b860a69d67d15d,
'@/components/afterNavLinks/LinkToCustomDefaultView#LinkToCustomDefaultView':
LinkToCustomDefaultView_4c5f581c8bfa951ce2f83c24c4f36b3b,
'@/components/views/CustomRootView#CustomRootView':
CustomRootView_1ebb91ef5ff1ea4dc9a27ceb8e9ee0ab,
'@/components/views/CustomDefaultRootView#CustomDefaultRootView':
CustomDefaultRootView_a2f8ce99b3a1692f7ec03a907e1ea4ce,
'@/components/views/CustomMinimalRootView#CustomMinimalRootView':
CustomMinimalRootView_9211f699dea5524a957f33011b786586,
}

View File

@@ -5,5 +5,5 @@ import { ArrayField } from '@payloadcms/ui'
import React from 'react'
export const CustomArrayFieldClient: ArrayFieldClientComponent = (props) => {
return <ArrayField field={props?.field} path={props?.path} permissions={props?.permissions} />
return <ArrayField {...props} />
}

View File

@@ -4,6 +4,6 @@ import type { ArrayFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomArrayFieldLabelClient: ArrayFieldLabelClientComponent = (props) => {
return <FieldLabel label={props?.label} path={props?.path} />
export const CustomArrayFieldLabelClient: ArrayFieldLabelClientComponent = ({ field, path }) => {
return <FieldLabel label={field?.label || field?.name} path={path} required={field?.required} />
}

View File

@@ -3,7 +3,13 @@ import type React from 'react'
import { ArrayField } from '@payloadcms/ui'
export const CustomArrayFieldServer: ArrayFieldServerComponent = (props) => {
const path = (props?.path || props?.field?.name || '') as string
return <ArrayField field={props.clientField} path={path} permissions={props?.permissions} />
export const CustomArrayFieldServer: ArrayFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<ArrayField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
)
}

View File

@@ -3,6 +3,15 @@ import type { ArrayFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomArrayFieldLabelServer: ArrayFieldLabelServerComponent = (props) => {
return <FieldLabel label={props?.label} />
export const CustomArrayFieldLabelServer: ArrayFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
required={clientField?.required}
path={path}
/>
)
}

View File

@@ -5,5 +5,5 @@ import { BlocksField } from '@payloadcms/ui'
import React from 'react'
export const CustomBlocksFieldClient: BlocksFieldClientComponent = (props) => {
return <BlocksField field={props?.field} path={props.path} permissions={props?.permissions} />
return <BlocksField {...props} />
}

View File

@@ -4,6 +4,6 @@ import type { BlocksFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomBlocksFieldLabelClient: BlocksFieldLabelClientComponent = (props) => {
return <FieldLabel label={props?.label} />
export const CustomBlocksFieldLabelClient: BlocksFieldLabelClientComponent = ({ field, path }) => {
return <FieldLabel label={field?.label || field?.name} path={path} required={field?.required} />
}

View File

@@ -3,7 +3,18 @@ import type React from 'react'
import { BlocksField } from '@payloadcms/ui'
export const CustomBlocksFieldServer: BlocksFieldServerComponent = (props) => {
const path = (props?.path || props?.field?.name || '') as string
return <BlocksField field={props?.clientField} path={path} permissions={props?.permissions} />
export const CustomBlocksFieldServer: BlocksFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<BlocksField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}

View File

@@ -3,6 +3,15 @@ import type { BlocksFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomBlocksFieldLabelServer: BlocksFieldLabelServerComponent = (props) => {
return <FieldLabel label={props?.label} />
export const CustomBlocksFieldLabelServer: BlocksFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}

View File

@@ -5,5 +5,5 @@ import { CheckboxField } from '@payloadcms/ui'
import React from 'react'
export const CustomCheckboxFieldClient: CheckboxFieldClientComponent = (props) => {
return <CheckboxField field={props?.field} path={props?.path} />
return <CheckboxField {...props} />
}

View File

@@ -4,6 +4,9 @@ import type { CheckboxFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomCheckboxFieldLabelClient: CheckboxFieldLabelClientComponent = (props) => {
return <FieldLabel label={props?.label} path={props?.path} />
export const CustomCheckboxFieldLabelClient: CheckboxFieldLabelClientComponent = ({
field,
path,
}) => {
return <FieldLabel label={field?.label || field?.name} path={path} required={field?.required} />
}

View File

@@ -3,7 +3,18 @@ import type React from 'react'
import { CheckboxField } from '@payloadcms/ui'
export const CustomCheckboxFieldServer: CheckboxFieldServerComponent = (props) => {
const path = (props?.path || props?.field?.name || '') as string
return <CheckboxField field={props?.clientField} path={path} />
export const CustomCheckboxFieldServer: CheckboxFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<CheckboxField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}

View File

@@ -3,6 +3,15 @@ import type { CheckboxFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomCheckboxFieldLabelServer: CheckboxFieldLabelServerComponent = (props) => {
return <FieldLabel label={props?.label} path={props?.path} />
export const CustomCheckboxFieldLabelServer: CheckboxFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}

View File

@@ -0,0 +1,9 @@
'use client'
import type { CodeFieldClientComponent } from 'payload'
import { CodeField } from '@payloadcms/ui'
import React from 'react'
export const CustomCodeFieldClient: CodeFieldClientComponent = (props) => {
return <CodeField {...props} />
}

View File

@@ -0,0 +1,9 @@
'use client'
import type { CodeFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomCodeFieldLabelClient: CodeFieldLabelClientComponent = ({ field, path }) => {
return <FieldLabel label={field?.label || field?.name} path={path} required={field?.required} />
}

View File

@@ -0,0 +1,15 @@
import type { CodeFieldServerComponent } from 'payload'
import type React from 'react'
import { CodeField } from '@payloadcms/ui'
export const CustomCodeFieldServer: CodeFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<CodeField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
)
}

View File

@@ -0,0 +1,17 @@
import type { CodeFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomCodeFieldLabelServer: CodeFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}

View File

@@ -0,0 +1,24 @@
import type { CollectionConfig } from 'payload'
export const codeFields: CollectionConfig['fields'] = [
{
name: 'codeFieldServerComponent',
type: 'code',
admin: {
components: {
Field: '@/collections/Fields/code/components/server/Field#CustomCodeFieldServer',
Label: '@/collections/Fields/code/components/server/Label#CustomCodeFieldLabelServer',
},
},
},
{
name: 'codeFieldClientComponent',
type: 'code',
admin: {
components: {
Field: '@/collections/Fields/code/components/client/Field#CustomCodeFieldClient',
Label: '@/collections/Fields/code/components/client/Label#CustomCodeFieldLabelClient',
},
},
},
]

View File

@@ -5,5 +5,5 @@ import { DateTimeField } from '@payloadcms/ui'
import React from 'react'
export const CustomDateFieldClient: DateFieldClientComponent = (props) => {
return <DateTimeField field={props?.field} path={props?.path} />
return <DateTimeField {...props} />
}

View File

@@ -4,6 +4,6 @@ import type { DateFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomDateFieldLabelClient: DateFieldLabelClientComponent = (props) => {
return <FieldLabel label={props?.label} path={props?.path} />
export const CustomDateFieldLabelClient: DateFieldLabelClientComponent = ({ field, path }) => {
return <FieldLabel label={field?.label || field?.name} path={path} required={field?.required} />
}

View File

@@ -3,7 +3,18 @@ import type React from 'react'
import { DateTimeField } from '@payloadcms/ui'
export const CustomDateFieldServer: DateFieldServerComponent = (props) => {
const path = (props?.path || props?.field?.name || '') as string
return <DateTimeField field={props?.clientField} path={path} />
export const CustomDateFieldServer: DateFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<DateTimeField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}

View File

@@ -3,6 +3,15 @@ import type { DateFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomDateFieldLabelServer: DateFieldLabelServerComponent = (props) => {
return <FieldLabel label={props?.label} />
export const CustomDateFieldLabelServer: DateFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}

View File

@@ -5,5 +5,5 @@ import { EmailField } from '@payloadcms/ui'
import React from 'react'
export const CustomEmailFieldClient: EmailFieldClientComponent = (props) => {
return <EmailField field={props?.field} path={props?.path} />
return <EmailField {...props} />
}

View File

@@ -4,6 +4,6 @@ import type { EmailFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomEmailFieldLabelClient: EmailFieldLabelClientComponent = (props) => {
return <FieldLabel label={props?.label} />
export const CustomEmailFieldLabelClient: EmailFieldLabelClientComponent = ({ field, path }) => {
return <FieldLabel label={field?.label || field?.name} path={path} required={field?.required} />
}

View File

@@ -3,7 +3,13 @@ import type React from 'react'
import { EmailField } from '@payloadcms/ui'
export const CustomEmailFieldServer: EmailFieldServerComponent = (props) => {
const path = (props?.path || props?.field?.name || '') as string
return <EmailField field={props?.clientField} path={path} />
export const CustomEmailFieldServer: EmailFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<EmailField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
)
}

View File

@@ -3,6 +3,15 @@ import type { EmailFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomEmailFieldLabelServer: EmailFieldLabelServerComponent = (props) => {
return <FieldLabel label={props?.label} />
export const CustomEmailFieldLabelServer: EmailFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}

View File

@@ -3,8 +3,10 @@ import type { CollectionConfig, Field } from 'payload'
import { arrayFields } from './array'
import { blocksFields } from './blocks'
import { checkboxFields } from './checkbox'
import { codeFields } from './code'
import { dateFields } from './date'
import { emailFields } from './email'
import { jsonFields } from './json'
import { numberFields } from './number'
import { pointFields } from './point'
import { radioFields } from './radio'
@@ -28,8 +30,10 @@ export const CustomFields: CollectionConfig = {
arrayFields,
blocksFields,
checkboxFields,
codeFields,
dateFields,
emailFields,
jsonFields,
numberFields,
pointFields,
radioFields,

View File

@@ -0,0 +1,9 @@
'use client'
import type { JSONFieldClientComponent } from 'payload'
import { JSONField } from '@payloadcms/ui'
import React from 'react'
export const CustomJSONFieldClient: JSONFieldClientComponent = (props) => {
return <JSONField {...props} />
}

View File

@@ -0,0 +1,9 @@
'use client'
import type { JSONFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomJSONFieldLabelClient: JSONFieldLabelClientComponent = ({ field, path }) => {
return <FieldLabel label={field?.label || field?.name} path={path} required={field?.required} />
}

View File

@@ -0,0 +1,15 @@
import type { JSONFieldServerComponent } from 'payload'
import type React from 'react'
import { JSONField } from '@payloadcms/ui'
export const CustomJSONFieldServer: JSONFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<JSONField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
)
}

View File

@@ -0,0 +1,17 @@
import type { JSONFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomJSONFieldLabelServer: JSONFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}

View File

@@ -0,0 +1,24 @@
import type { CollectionConfig } from 'payload'
export const jsonFields: CollectionConfig['fields'] = [
{
name: 'jsonFieldServerComponent',
type: 'json',
admin: {
components: {
Field: '@/collections/Fields/json/components/server/Field#CustomJSONFieldServer',
Label: '@/collections/Fields/json/components/server/Label#CustomJSONFieldLabelServer',
},
},
},
{
name: 'jsonFieldClientComponent',
type: 'json',
admin: {
components: {
Field: '@/collections/Fields/json/components/client/Field#CustomJSONFieldClient',
Label: '@/collections/Fields/json/components/client/Label#CustomJSONFieldLabelClient',
},
},
},
]

View File

@@ -5,5 +5,5 @@ import { NumberField } from '@payloadcms/ui'
import React from 'react'
export const CustomNumberFieldClient: NumberFieldClientComponent = (props) => {
return <NumberField field={props?.field} path={props?.path} />
return <NumberField {...props} />
}

View File

@@ -4,6 +4,6 @@ import type { NumberFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomNumberFieldLabelClient: NumberFieldLabelClientComponent = (props) => {
return <FieldLabel label={props?.label} path={props?.path} />
export const CustomNumberFieldLabelClient: NumberFieldLabelClientComponent = ({ field, path }) => {
return <FieldLabel label={field?.label || field?.name} path={path} required={field?.required} />
}

View File

@@ -3,7 +3,18 @@ import type React from 'react'
import { NumberField } from '@payloadcms/ui'
export const CustomNumberFieldServer: NumberFieldServerComponent = (props) => {
const path = (props?.path || props?.field?.name || '') as string
return <NumberField field={props?.clientField} path={path} />
export const CustomNumberFieldServer: NumberFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<NumberField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}

View File

@@ -3,6 +3,15 @@ import type { NumberFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomNumberFieldLabelServer: NumberFieldLabelServerComponent = (props) => {
return <FieldLabel label={props?.label} />
export const CustomNumberFieldLabelServer: NumberFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}

View File

@@ -5,5 +5,5 @@ import { PointField } from '@payloadcms/ui'
import React from 'react'
export const CustomPointFieldClient: PointFieldClientComponent = (props) => {
return <PointField field={props?.field} path={props?.path} />
return <PointField {...props} />
}

View File

@@ -4,6 +4,6 @@ import type { PointFieldLabelClientComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomPointFieldLabelClient: PointFieldLabelClientComponent = (props) => {
return <FieldLabel label={props?.label} path={props?.path} />
export const CustomPointFieldLabelClient: PointFieldLabelClientComponent = ({ field, path }) => {
return <FieldLabel label={field?.label || field?.name} path={path} required={field?.required} />
}

View File

@@ -3,7 +3,13 @@ import type React from 'react'
import { PointField } from '@payloadcms/ui'
export const CustomPointFieldServer: PointFieldServerComponent = (props) => {
const path = (props?.path || props?.field?.name || '') as string
return <PointField field={props?.clientField} path={path} />
export const CustomPointFieldServer: PointFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<PointField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
)
}

View File

@@ -3,6 +3,15 @@ import type { PointFieldLabelServerComponent } from 'payload'
import { FieldLabel } from '@payloadcms/ui'
import React from 'react'
export const CustomPointFieldLabelServer: PointFieldLabelServerComponent = (props) => {
return <FieldLabel label={props?.label} />
export const CustomPointFieldLabelServer: PointFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}

View File

@@ -5,5 +5,5 @@ import { RadioGroupField } from '@payloadcms/ui'
import React from 'react'
export const CustomRadioFieldClient: RadioFieldClientComponent = (props) => {
return <RadioGroupField field={props?.field} path={props?.path} />
return <RadioGroupField {...props} />
}

Some files were not shown because too many files have changed in this diff Show More