docs: new and improve lexical docs, hoist up all headings (#6639)
This commit is contained in:
@@ -8,8 +8,6 @@ keywords: globals, access control, permissions, documentation, Content Managemen
|
||||
|
||||
You can define Global-level Access Control within each Global's `access` property. All Access Control functions accept one `args` argument.
|
||||
|
||||
\*\*Available argument properties:
|
||||
|
||||
## Available Controls
|
||||
|
||||
| Function | Allows/Denies Access |
|
||||
|
||||
@@ -19,7 +19,7 @@ Access control within Payload is extremely powerful while remaining easy and int
|
||||
- Restricting a `User` to only be able to see their own `Order`(s), but no others
|
||||
- Allowing `User`s that belong to a certain `Organization` to access only that `Organization`'s `Resource`s
|
||||
|
||||
### Default Settings
|
||||
## Default Settings
|
||||
|
||||
**By default, all Collections and Globals require that a user is logged in to be able to interact in any way.** The default Access Control function evaluates the `user` from the Express `req` and returns `true` if a user is logged in, and `false` if not.
|
||||
|
||||
@@ -43,7 +43,7 @@ const defaultPayloadAccess = ({ req: { user } }) => {
|
||||
to <strong>false</strong>.
|
||||
</Banner>
|
||||
|
||||
### Access Control Types
|
||||
## Access Control Types
|
||||
|
||||
You can manage access within Payload on three different levels:
|
||||
|
||||
@@ -51,7 +51,7 @@ You can manage access within Payload on three different levels:
|
||||
- [Fields](/docs/access-control/fields)
|
||||
- [Globals](/docs/access-control/globals)
|
||||
|
||||
### When Access Control is Executed
|
||||
## When Access Control is Executed
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
@@ -60,17 +60,17 @@ You can manage access within Payload on three different levels:
|
||||
your access control is executed.
|
||||
</Banner>
|
||||
|
||||
#### As you execute operations
|
||||
### As you execute operations
|
||||
|
||||
When you perform Payload operations like `create`, `read`, `update`, and `delete`, your access control functions will be executed before any changes or operations are completed.
|
||||
|
||||
#### Within the Admin UI
|
||||
### Within the Admin UI
|
||||
|
||||
The Payload Admin UI responds dynamically to the access control that you define. For example, if you restrict editing a `ExampleCollection` to only users that feature a `role` of `admin`, the Payload Admin UI will **hide** the `ExampleCollection` from the Admin UI entirely. This is super powerful and allows you to control who can do what with your Admin UI.
|
||||
|
||||
To accomplish this, Payload ships with an `Access` operation, which is executed when a user logs into the Admin UI. Payload will execute each one of your access control functions, across all collections, globals, and fields, at the top level and return a response that contains a reflection of what the currently authenticated user can do with your application.
|
||||
|
||||
### Argument Availability
|
||||
## Argument Availability
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
|
||||
@@ -7,7 +7,7 @@ desc: Bundlers are used to bundle the code that serves Payload's Admin Panel.
|
||||
|
||||
Payload has two official bundlers, the [Webpack Bundler](/docs/admin/webpack) and the [Vite Bundler](/docs/admin/vite). You must install a bundler to use the admin panel.
|
||||
|
||||
##### Install a bundler
|
||||
## Install a bundler
|
||||
|
||||
Webpack (recommended):
|
||||
|
||||
@@ -21,7 +21,7 @@ Vite (beta):
|
||||
yarn add @payloadcms/bundler-vite
|
||||
```
|
||||
|
||||
##### Configure the bundler
|
||||
## Configure the bundler
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
@@ -39,7 +39,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### What are bundlers?
|
||||
## What are bundlers?
|
||||
|
||||
At their core, a bundler's main goal is to take a bunch of files and turn them into a few optimized files that you ship to the browser. The admin UI has a root `index.html` entry point, and from there the bundler traverses the dependency tree, bundling all of the files that are required from that point on.
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ To swap in your own React component, first, consult the list of available compon
|
||||
normally accepts.
|
||||
</Banner>
|
||||
|
||||
### Base Component Overrides
|
||||
## Base Component Overrides
|
||||
|
||||
You can override a set of admin panel-wide components by providing a component to your base Payload config's `admin.components` property. The following options are available:
|
||||
|
||||
| Path | Description |
|
||||
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`Nav`** | Contains the sidebar / mobile menu in its entirety. |
|
||||
| **`BeforeNavLinks`** | Array of components to inject into the built-in Nav, _before_ the links themselves. |
|
||||
| **`AfterNavLinks`** | Array of components to inject into the built-in Nav, _after_ the links. |
|
||||
@@ -73,7 +73,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#### Views
|
||||
### Views
|
||||
|
||||
You can easily swap entire views with your own by using the `admin.components.views` property. At the root level, Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
@@ -111,7 +111,7 @@ For more granular control, pass a configuration object instead. Each view corres
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
#### Adding new views
|
||||
### Adding new views
|
||||
|
||||
To add a _new_ view to the Admin Panel, simply add another key to the `views` object with at least a `path` and `Component` property. For example:
|
||||
|
||||
@@ -145,7 +145,7 @@ _For more examples regarding how to customize components, look at the following
|
||||
|
||||
For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component).
|
||||
|
||||
### Collections
|
||||
## Collections
|
||||
|
||||
You can override components on a collection-by-collection basis via the `admin.components` property.
|
||||
|
||||
@@ -221,7 +221,7 @@ export const MyCollection: SanitizedCollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
#### Collection views
|
||||
### Collection views
|
||||
|
||||
To swap out entire views on collections, you can use the `admin.components.views` property on the collection's config. Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
@@ -297,7 +297,7 @@ Here's an example of how you can customize nested views within the `Edit` view i
|
||||
|
||||
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
|
||||
|
||||
### Globals
|
||||
## Globals
|
||||
|
||||
As with Collections, you can override components on a global-by-global basis via the `admin.components` property.
|
||||
|
||||
@@ -309,7 +309,7 @@ As with Collections, you can override components on a global-by-global basis via
|
||||
| **`elements.PreviewButton`** | Replace the default `Preview` button with a custom component. |
|
||||
| **`views`** | Override or create new views within the Payload Admin UI. [More](#global-views) |
|
||||
|
||||
#### Global views
|
||||
### Global views
|
||||
|
||||
To swap out views for globals, you can use the `admin.components.views` property on the global's config. Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
@@ -378,7 +378,7 @@ Here's how you can customize nested views within the `Edit` view in Globals, inc
|
||||
|
||||
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
|
||||
|
||||
### Custom Tabs
|
||||
## Custom Tabs
|
||||
|
||||
You can easily swap individual collection or global edit views. To do this, pass an _object_ to the `admin.components.views.Edit` property of the config. Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
@@ -445,7 +445,7 @@ export const MyCollection: SanitizedCollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Building a custom view component
|
||||
## Building a custom view component
|
||||
|
||||
Your custom view components will be given all the props that a React Router `<Route />` typically would receive, as well as two props from Payload:
|
||||
|
||||
@@ -470,7 +470,7 @@ You can find examples of custom views in the [Payload source code `/test/admin/c
|
||||
|
||||
To see how to pass in your custom views to create custom views of your own, take a look at the `admin.components.views` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/main/test/admin/config.ts).
|
||||
|
||||
### Fields
|
||||
## Fields
|
||||
|
||||
All Payload fields support the ability to swap in your own React components. So, for example, instead of rendering a default Text input, you might need to render a color picker that provides the editor with a custom color picker interface to restrict the data entered to colors only.
|
||||
|
||||
@@ -531,7 +531,7 @@ const CustomCell: React.FC<Props> = (props) => {
|
||||
|
||||
When writing your own custom components you can make use of a number of hooks to set data, get reactive changes to other fields, get the id of the document or interact with a context from a custom provider.
|
||||
|
||||
### Sending and receiving values from the form
|
||||
## Sending and receiving values from the form
|
||||
|
||||
When swapping out the `Field` component, you'll be responsible for sending and receiving the field's `value` from the form itself. To do so, import the `useField` hook as follows:
|
||||
|
||||
@@ -669,7 +669,7 @@ As your admin customizations gets more complex you may want to share state betwe
|
||||
component for the admin UI to show
|
||||
</Banner>
|
||||
|
||||
### Styling Custom Components
|
||||
## Styling Custom Components
|
||||
|
||||
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-ini styling, so it blends more thoroughly into the existing admin UI.
|
||||
|
||||
@@ -679,7 +679,7 @@ To make use of Payload SCSS variables / mixins to use directly in your own compo
|
||||
@import '~payload/scss';
|
||||
```
|
||||
|
||||
### Getting the current language
|
||||
## Getting the current language
|
||||
|
||||
When developing custom components you can support multiple languages to be consistent with Payload's i18n support. The best way to do this is to add your translation resources to the [i18n configuration](https://payloadcms.com/docs/configuration/i18n) and import `useTranslation` from `@payloadcms/ui/providers/Translation` in your components.
|
||||
|
||||
@@ -703,7 +703,7 @@ const CustomComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### Getting the current locale
|
||||
## Getting the current locale
|
||||
|
||||
In any custom component you can get the selected locale with `useLocale` hook. `useLocale` returns the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Customize your Payload admin panel further by adding your own CSS or SCSS
|
||||
keywords: admin, css, scss, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
### Adding your own CSS / SCSS
|
||||
## Adding your own CSS / SCSS
|
||||
|
||||
You can add your own CSS by providing your base Payload config with a path to your own CSS or SCSS. Customize the styling of any part of the Payload dashboard as necessary.
|
||||
|
||||
@@ -25,7 +25,7 @@ const config = buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Overriding built-in styles
|
||||
## Overriding built-in styles
|
||||
|
||||
To make it as easy as possible for you to override our styles, Payload uses [BEM naming conventions](http://getbem.com/) for all CSS within the Admin UI. If you provide your own CSS, you can override any built-in styles easily.
|
||||
|
||||
@@ -41,7 +41,7 @@ You can find the built-in Payload CSS variables within [`./src/admin/scss/app.sc
|
||||
- Fonts
|
||||
- Horizontal gutter
|
||||
|
||||
#### Dark mode
|
||||
### Dark mode
|
||||
|
||||
<Banner type="warning">
|
||||
If you're overriding colors or theme elevations, make sure to consider how your changes will
|
||||
|
||||
@@ -10,7 +10,7 @@ It's common for your config to rely on server only modules to perform logic in a
|
||||
|
||||
Any file that imports a server-only module such as `fs`, `stripe`, `authorizenet`, `nodemailer`, etc. **cannot** be included in the browser bundle.
|
||||
|
||||
#### Example Scenario
|
||||
## Example Scenario
|
||||
|
||||
Say we have a collection called `Subscriptions` that has a `beforeChange` hook that creates a Stripe subscription whenever a Subscription document is created in Payload.
|
||||
|
||||
@@ -79,7 +79,7 @@ export const createStripeSubscription = async ({ data, operation }) => {
|
||||
|
||||
**As-is, this collection will prevent your Admin panel from bundling or loading correctly, because Stripe relies on some Node-only packages.**
|
||||
|
||||
#### How to fix this
|
||||
## How to exclude server-only code
|
||||
|
||||
You need to make sure that you use `alias`es to tell your bundler to import "safe" files vs. attempting to import any server-side code that you need to get rid of. Depending on your bundler (Webpack, Vite, etc.) the steps involved may be slightly different.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: admin, components, custom, documentation, Content Management System, c
|
||||
|
||||
Payload provides a variety of powerful hooks that can be used within your own React components. With them, you can interface with Payload itself and build just about any type of complex customization you can think of—directly in familiar React code.
|
||||
|
||||
### useField
|
||||
## useField
|
||||
|
||||
The `useField` hook is used internally within every applicable Payload field component, and it manages sending and receiving a field's state from its parent form.
|
||||
|
||||
@@ -52,7 +52,7 @@ const {
|
||||
// The rest of your component goes here
|
||||
```
|
||||
|
||||
### useFormFields
|
||||
## useFormFields
|
||||
|
||||
There are times when a custom field component needs to have access to data from other fields, and you have a few options to do so. The `useFormFields` hook is a powerful and highly performant way to retrieve a form's field state, as well as to retrieve the `dispatchFields` method, which can be helpful for setting other fields' form states from anywhere within a form.
|
||||
|
||||
@@ -81,7 +81,7 @@ const MyComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### useAllFormFields
|
||||
## useAllFormFields
|
||||
|
||||
**To retrieve more than one field**, you can use the `useAllFormFields` hook. Your component will re-render when _any_ field changes, so use this hook only if you absolutely need to. Unlike the `useFormFields` hook, this hook does not accept a "selector", and it always returns an array with type of `[fields: Fields, dispatch: React.Dispatch<Action>]]`.
|
||||
|
||||
@@ -109,7 +109,7 @@ const ExampleComponent: React.FC = () => {
|
||||
};
|
||||
```
|
||||
|
||||
##### Updating other fields' values
|
||||
#### Updating other fields' values
|
||||
|
||||
If you are building a custom component, then you should use `setValue` which is returned from the `useField` hook to programmatically set your field's value. But if you're looking to update _another_ field's value, you can use `dispatchFields` returned from `useFormFields`.
|
||||
|
||||
@@ -128,7 +128,7 @@ You can send the following actions to the `dispatchFields` function.
|
||||
|
||||
To see types for each action supported within the `dispatchFields` hook, check out the Form types [here](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/components/forms/Form/types.ts).
|
||||
|
||||
### useForm
|
||||
## useForm
|
||||
|
||||
The `useForm` hook can be used to interact with the form itself, and sends back many methods that can be used to reactively fetch form state without causing rerenders within your components each time a field is changed. This is useful if you have action-based callbacks that your components fire, and need to interact with form state _based on a user action_.
|
||||
|
||||
@@ -635,7 +635,7 @@ export const CustomArrayManager = () => {
|
||||
]}
|
||||
/>
|
||||
|
||||
### useCollapsible
|
||||
## useCollapsible
|
||||
|
||||
The `useCollapsible` hook allows you to control parent collapsibles:
|
||||
|
||||
@@ -667,7 +667,7 @@ const CustomComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### useDocumentInfo
|
||||
## useDocumentInfo
|
||||
|
||||
The `useDocumentInfo` hook provides lots of information about the document currently being edited, including the following:
|
||||
|
||||
@@ -707,7 +707,7 @@ const LinkFromCategoryToPosts: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### useLocale
|
||||
## 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:
|
||||
|
||||
@@ -728,7 +728,7 @@ const Greeting: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### useAuth
|
||||
## useAuth
|
||||
|
||||
Useful to retrieve info about the currently logged in user as well as methods for interacting with it. It sends back an object with the following properties:
|
||||
|
||||
@@ -755,7 +755,7 @@ const Greeting: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### useConfig
|
||||
## useConfig
|
||||
|
||||
Used to easily fetch the full Payload config.
|
||||
|
||||
@@ -771,7 +771,7 @@ const MyComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### useEditDepth
|
||||
## useEditDepth
|
||||
|
||||
Sends back how many editing levels "deep" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.
|
||||
|
||||
@@ -787,11 +787,11 @@ const MyComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### usePreferences
|
||||
## usePreferences
|
||||
|
||||
Returns methods to set and get user preferences. More info can be found [here](https://payloadcms.com/docs/admin/preferences).
|
||||
|
||||
### useTheme
|
||||
## useTheme
|
||||
|
||||
Returns the currently selected theme (`light`, `dark` or `auto`), a set function to update it and a boolean `autoMode`, used to determine if the theme value should be set automatically based on the user's device preferences.
|
||||
|
||||
@@ -819,7 +819,7 @@ const MyComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### useTableColumns
|
||||
## useTableColumns
|
||||
|
||||
Returns methods to manipulate table columns
|
||||
|
||||
@@ -843,7 +843,7 @@ const MyComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
### useDocumentEvents
|
||||
## useDocumentEvents
|
||||
|
||||
The `useDocumentEvents` hook provides a way of subscribing to cross-document events, such as updates made to nested documents within a drawer. This hook will report document events that are outside the scope of the document currently being edited. This hook provides the following:
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Out of the box, Payload handles the persistence of your users' preferences in a
|
||||
that is reading or setting a preference via all provided authentication methods.
|
||||
</Banner>
|
||||
|
||||
### Use cases
|
||||
## Use cases
|
||||
|
||||
This API is used significantly for internal operations of the Admin panel, as mentioned above. But, if you're building your own React components for use in the Admin panel, you can allow users to set their own preferences in correspondence to their usage of your components. For example:
|
||||
|
||||
@@ -30,7 +30,7 @@ This API is used significantly for internal operations of the Admin panel, as me
|
||||
- You might want to store `recentlyAccessed` documents to give admin editors an easy shortcut back to their recently accessed documents on the `Dashboard` or similar
|
||||
- Many other use cases exist. Invent your own! Give your editors an intelligent and persistent editing experience.
|
||||
|
||||
### Database
|
||||
## Database
|
||||
|
||||
Payload automatically creates an internally used `payload-preferences` collection that stores user preferences. Each document in the `payload-preferences` collection contains the following shape:
|
||||
|
||||
@@ -44,15 +44,15 @@ Payload automatically creates an internally used `payload-preferences` collectio
|
||||
| `createdAt` | A timestamp of when the preference was created. |
|
||||
| `updatedAt` | A timestamp set to the last time the preference was updated. |
|
||||
|
||||
### APIs
|
||||
## APIs
|
||||
|
||||
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#) APIs.
|
||||
|
||||
### Adding or reading Preferences in your own components
|
||||
## Adding or reading Preferences in your own components
|
||||
|
||||
The Payload admin panel offers a `usePreferences` hook. The hook is only meant for use within the admin panel itself. It provides you with two methods:
|
||||
|
||||
##### `getPreference`
|
||||
#### `getPreference`
|
||||
|
||||
This async method provides an easy way to retrieve a user's preferences by `key`. It will return a promise containing the resulting preference value.
|
||||
|
||||
@@ -60,7 +60,7 @@ This async method provides an easy way to retrieve a user's preferences by `key`
|
||||
|
||||
- `key`: the `key` of your preference to retrieve.
|
||||
|
||||
##### `setPreference`
|
||||
#### `setPreference`
|
||||
|
||||
Also async, this method provides you with an easy way to set a user preference. It returns `void`.
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ That plugin should create an alias to support Vite as follows:
|
||||
|
||||
This will effectively alias the entire plugin and work with Vite. If the plugin requires admin-specific code, then the `./my-admin-plugin.js` alias target file should reflect any changes necessary to the admin UI that the main server-side plugin performs.
|
||||
|
||||
### Extending the Vite config
|
||||
## Extending the Vite config
|
||||
|
||||
The Payload config supports a new property for plugins to be able to extend the Vite config specifically. That property exists on the main Payload config under `admin.vite`. You can check out the [Vite docs](https://vitejs.dev/config/shared-options.html) for more information on what you can do with the Vite config.
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@ Payload has a Webpack (v5) bundler that you can build the Admin panel with. For
|
||||
|
||||
Out of the box, the Webpack bundler supports common functionalities such as SCSS and Typescript, but there are many cases where you may need to add support for additional functionalities.
|
||||
|
||||
#### Installation
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
yarn add @payloadcms/bundler-webpack
|
||||
```
|
||||
|
||||
#### Import the bundler
|
||||
### Import the bundler
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
@@ -33,7 +33,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Extending Webpack
|
||||
## Extending Webpack
|
||||
|
||||
If you need to extend the Webpack config, you can do so by passing a function to the `admin.webpack` property on your Payload config.
|
||||
The function will receive the Webpack config as an argument and should return the modified config.
|
||||
|
||||
@@ -13,7 +13,7 @@ To enable Authentication on a collection, define an `auth` property and set it t
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More](/docs/authentication/config#api-keys) |
|
||||
| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |
|
||||
| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: authentication, config, configuration, documentation, Content Manageme
|
||||
|
||||
Enabling Authentication on a Collection automatically exposes additional auth-based operations in the Local, REST, and GraphQL APIs.
|
||||
|
||||
### Access
|
||||
## Access
|
||||
|
||||
The Access operation returns what a logged in user can and can't do with the collections and globals that are registered via your config. This data can be immensely helpful if your app needs to show and hide certain features based on access control, as the Payload Admin panel does.
|
||||
|
||||
@@ -69,7 +69,7 @@ query {
|
||||
|
||||
Document access can also be queried on a collection/global basis. Access on a global can queried like `http://localhost:3000/api/global-slug/access`, Collection document access can be queried like `http://localhost:3000/api/collection-slug/access/:id`.
|
||||
|
||||
### Me
|
||||
## Me
|
||||
|
||||
Returns either a logged in user with token or null when there is no logged in user.
|
||||
|
||||
@@ -105,7 +105,7 @@ query {
|
||||
}
|
||||
```
|
||||
|
||||
### Login
|
||||
## Login
|
||||
|
||||
Accepts an `email` and `password`. On success, it will return the logged in user as well as a token that can be used to authenticate. In the GraphQL and REST APIs, this operation also automatically sets an HTTP-only cookie including the user's token. If you pass an Express `res` to the Local API operation, Payload will set a cookie there as well.
|
||||
|
||||
@@ -166,7 +166,7 @@ const result = await payload.login({
|
||||
})
|
||||
```
|
||||
|
||||
### Logout
|
||||
## Logout
|
||||
|
||||
As Payload sets HTTP-only cookies, logging out cannot be done by just removing a cookie in JavaScript, as HTTP-only cookies are inaccessible by JS within the browser. So, Payload exposes a `logout` operation to delete the token in a safe way.
|
||||
|
||||
@@ -189,7 +189,7 @@ mutation {
|
||||
}
|
||||
```
|
||||
|
||||
### Refresh
|
||||
## Refresh
|
||||
|
||||
Allows for "refreshing" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by sending the operation the token that is about to expire.
|
||||
|
||||
@@ -244,7 +244,7 @@ mutation {
|
||||
`token` arg.
|
||||
</Banner>
|
||||
|
||||
### Verify by Email
|
||||
## Verify by Email
|
||||
|
||||
If your collection supports email verification, the Verify operation will be exposed which accepts a verification token and sets the user's `_verified` property to `true`, thereby allowing the user to authenticate with the Payload API.
|
||||
|
||||
@@ -276,7 +276,7 @@ const result = await payload.verifyEmail({
|
||||
})
|
||||
```
|
||||
|
||||
### Unlock
|
||||
## Unlock
|
||||
|
||||
If a user locks themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The Admin panel features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation.
|
||||
|
||||
@@ -309,7 +309,7 @@ const result = await payload.unlock({
|
||||
})
|
||||
```
|
||||
|
||||
### Forgot Password
|
||||
## Forgot Password
|
||||
|
||||
Payload comes with built-in forgot password functionality. Submitting an email address to the Forgot Password operation will generate an email and send it to the respective email address with a link to reset their password.
|
||||
|
||||
@@ -361,7 +361,7 @@ const token = await payload.forgotPassword({
|
||||
use the token to "reset" their password.
|
||||
</Banner>
|
||||
|
||||
### Reset Password
|
||||
## Reset Password
|
||||
|
||||
After a user has "forgotten" their password and a token is generated, that token can be used to send to the reset password operation along with a new password which will allow the user to reset their password securely.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ _Admin panel screenshot depicting an Admins Collection with Auth enabled_
|
||||
|
||||
By default, Payload provides you with a `User` collection that supports Authentication, which is used to access the Admin panel. But, you can add support to one or many Collections of your own. For more information on how to customize, override, or remove the default `User` collection, [click here](/docs/admin/overview#the-admin-user-collection).
|
||||
|
||||
### Enabling Auth on a collection
|
||||
## Enabling Auth on a collection
|
||||
|
||||
Every Payload Collection can opt-in to supporting Authentication by specifying the `auth` property on the Collection's config to either `true` or to an object containing `auth` options.
|
||||
|
||||
@@ -71,11 +71,11 @@ export const Admins: CollectionConfig = {
|
||||
|
||||
Once enabled, each document that is created within the Collection can be thought of as a `user` - who can make use of commonly required authentication functions such as logging in / out, resetting their password, and more.
|
||||
|
||||
### Logging in / out, resetting password, etc.
|
||||
## Logging in / out, resetting password, etc.
|
||||
|
||||
[Click here](/docs/authentication/operations) for a list of all automatically-enabled Auth operations, including `login`, `logout`, `refresh`, and others.
|
||||
|
||||
### Token-based auth
|
||||
## Token-based auth
|
||||
|
||||
Successfully logging in returns a `JWT` (JSON web token) which is how a user will identify themselves to Payload. By providing this JWT via either an HTTP-only cookie or an `Authorization: JWT` or `Authorization: Bearer` header, Payload will automatically identify the user and add its user JWT data to the Express `req`, which is available throughout Payload including within access control, hooks, and more.
|
||||
|
||||
@@ -89,15 +89,15 @@ You can specify what data gets encoded to the JWT token by setting `saveToJWT` t
|
||||
property.
|
||||
</Banner>
|
||||
|
||||
### HTTP-only cookies
|
||||
## HTTP-only cookies
|
||||
|
||||
Payload `login`, `logout`, and `refresh` operations make use of HTTP-only cookies for authentication purposes. HTTP-only cookies are a highly secure method of storing identifiable data on a user's device so that Payload can automatically recognize a returning user until their cookie expires. They are totally protected from common XSS attacks and cannot be read at all via JavaScript in the browser.
|
||||
|
||||
##### Automatic browser inclusion
|
||||
#### Automatic browser inclusion
|
||||
|
||||
Modern browsers automatically include `http-only` cookies when making requests directly to URLs—meaning that if you are running your API on http://example.com, and you have logged in and visit http://example.com/test-page, your browser will automatically include the Payload authentication cookie for you.
|
||||
|
||||
##### Using Fetch or other HTTP APIs
|
||||
#### Using Fetch or other HTTP APIs
|
||||
|
||||
However, if you use `fetch` or similar APIs to retrieve Payload resources from its REST or GraphQL API, you need to specify to include credentials (cookies).
|
||||
|
||||
@@ -121,7 +121,7 @@ For more about how to automatically include cookies in requests from your app to
|
||||
will still show HTTP-only cookies, even when JavaScript running on the page can't.
|
||||
</Banner>
|
||||
|
||||
### CSRF Protection
|
||||
## CSRF Protection
|
||||
|
||||
CSRF (cross-site request forgery) attacks are common and dangerous. By using an HTTP-only cookie, Payload removes many XSS vulnerabilities, however, CSRF attacks can still be possible.
|
||||
|
||||
@@ -159,7 +159,7 @@ const config = buildConfig({
|
||||
export default config
|
||||
```
|
||||
|
||||
### Identifying users via the Authorization Header
|
||||
## Identifying users via the Authorization Header
|
||||
|
||||
In addition to authenticating via an HTTP-only cookie, you can also identify users via the `Authorization` header on an HTTP request.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Quickly configure and deploy your Payload Cloud project in a few simple st
|
||||
keywords: configuration, config, settings, project, cloud, payload cloud, deploy, deployment
|
||||
---
|
||||
|
||||
### Select your plan
|
||||
## Select your plan
|
||||
|
||||
Once you have created a project, you will need to select your plan. This will determine the resources that are allocated to your project and the features that are available to you.
|
||||
|
||||
@@ -17,7 +17,7 @@ Once you have created a project, you will need to select your plan. This will de
|
||||
anytime.
|
||||
</Banner>
|
||||
|
||||
### Project Details
|
||||
## Project Details
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -26,7 +26,7 @@ Once you have created a project, you will need to select your plan. This will de
|
||||
| **Project Slug** | Choose a unique slug to identify your project. This needs to be unique for your team and you can change it any time. |
|
||||
| **Team** | Select the team you want to create the project under. If this is your first project, a personal team will be created for you automatically. You can modify your team settings and invite new members at any time from the Team Settings page. |
|
||||
|
||||
### Build Settings
|
||||
## Build Settings
|
||||
|
||||
If you are deploying a new project from a template, the following settings will be automatically configured for you. If you are using your own repository, you need to make sure your build settings are accurate for your project to deploy correctly.
|
||||
|
||||
@@ -39,7 +39,7 @@ If you are deploying a new project from a template, the following settings will
|
||||
| **Branch to Deploy** | Select the branch of your repository that you want to deploy from. This is the branch that will be used to build your project when you commit new changes. |
|
||||
| **Default Domain** | Set a default domain for your project. This must be unique and you will not able to change it. You can always add a custom domain later in your project settings. |
|
||||
|
||||
### Environment Variables
|
||||
## Environment Variables
|
||||
|
||||
Any of the features in Payload Cloud that require environment variables will automatically be provided to your application. If your app requires any custom environment variables, you can set them here.
|
||||
|
||||
@@ -49,7 +49,7 @@ Any of the features in Payload Cloud that require environment variables will aut
|
||||
[here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars).
|
||||
</Banner>
|
||||
|
||||
### Payment
|
||||
## Payment
|
||||
|
||||
Payment methods can be set per project and can be updated any time. You can use team’s default payment method, or add a new one. Modify your payment methods in your Project settings / Team settings.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Manage your Payload Cloud projects.
|
||||
keywords: cloud, payload cloud, projects, project, overview, database, file storage, build settings, environment variables, custom domains, email, developing locally
|
||||
---
|
||||
|
||||
### Overview
|
||||
## Overview
|
||||
|
||||
<Banner>
|
||||
The overview tab shows your most recent deployment, along with build and deployment logs. From
|
||||
@@ -18,19 +18,19 @@ keywords: cloud, payload cloud, projects, project, overview, database, file stor
|
||||

|
||||
_A screenshot of the Overview page for a Cloud project._
|
||||
|
||||
### Database
|
||||
## Database
|
||||
|
||||
Your Payload Cloud project comes with a MongoDB serverless Atlas DB instance or a Dedicated Atlas cluster, depending on your plan. To interact with your cloud database, you will be provided with a MongoDB connection string. This can be found under the **Database** tab of your project.
|
||||
|
||||
`mongodb+srv://your_connection_string`
|
||||
|
||||
### File Storage
|
||||
## File Storage
|
||||
|
||||
Payload Cloud gives you S3 file storage backed by Cloudflare as a CDN, and this plugin extends Payload so that all of your media will be stored in S3 rather than locally.
|
||||
|
||||
AWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/plugin-cloud) will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.
|
||||
|
||||
#### Accessing Files Outside of Payload Cloud
|
||||
### Accessing Files Outside of Payload Cloud
|
||||
|
||||
If you'd like to access your files outside of Payload Cloud, you'll need to retrieve some values from your project's settings and put them into your environment variables. In Payload Cloud, navigate to the File Storage tab and copy the values using the copy button. Put these values in your .env file. Also copy the Cognito Password value separately and put into your .env file as well.
|
||||
|
||||
@@ -50,11 +50,11 @@ PAYLOAD_CLOUD_COGNITO_PASSWORD=
|
||||
|
||||
The plugin will pick up these values and use them to access your files.
|
||||
|
||||
### Build Settings
|
||||
## Build Settings
|
||||
|
||||
You can update settings from your Project’s Settings tab. Changes to your build settings will trigger a redeployment of your project.
|
||||
|
||||
### Environment Variables
|
||||
## Environment Variables
|
||||
|
||||
From the Environment Variables page of the Settings tab, you can add, update and delete variables for use in your project. Like build settings, these changes will trigger a redeployment of your project.
|
||||
|
||||
@@ -64,7 +64,7 @@ From the Environment Variables page of the Settings tab, you can add, update and
|
||||
[here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars).
|
||||
</Banner>
|
||||
|
||||
### Custom Domains
|
||||
## Custom Domains
|
||||
|
||||
With Payload Cloud, you can add custom domain names to your project. To do so, first go to the Domains page of the Settings tab of your project. Here you can see your default domain. To add a new domain, type in the domain name you wish to use.
|
||||
|
||||
@@ -84,19 +84,19 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Email
|
||||
## Email
|
||||
|
||||
Powered by [Resend](https://resend.com), Payload Cloud comes with integrated email support out of the box. No configuration is needed, and you can use `payload.sendEmail()` to send email right from your Payload app. To learn more about sending email with Payload, checkout the [Email Configuration](https://payloadcms.com/docs/email/overview) overview.
|
||||
|
||||
If you are on the Pro or Enterprise plan, you can add your own custom Email domain name. From the Email page of your project’s Settings, add the domain you wish to use for email delivery. This will generate a set of DNS records. Add these records to your DNS provider and click verify to check that your records are resolving properly. Once verified, your emails will now be sent from your custom domain name.
|
||||
|
||||
### Developing Locally
|
||||
## Developing Locally
|
||||
|
||||
To make changes to your project, you will need to clone the repository defined in your project settings to your local machine. In order to run your project locally, you will need configure your local environment first. Refer to your repository’s `README.md` file to see the steps needed for your specific template.
|
||||
|
||||
From there, you are ready to make updates to your project. When you are ready to make your changes live, commit your changes to the branch you specified in your Project settings, and your application will automatically trigger a redeploy and build from your latest commit.
|
||||
|
||||
### Cloud Plugin
|
||||
## Cloud Plugin
|
||||
|
||||
Projects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload config:
|
||||
|
||||
@@ -117,7 +117,7 @@ export default buildConfig({
|
||||
over Payload Cloud's email service.
|
||||
</Banner>
|
||||
|
||||
##### **Optional configuration**
|
||||
#### **Optional configuration**
|
||||
|
||||
If you wish to opt-out of any Payload cloud features, the plugin also accepts options to do so.
|
||||
|
||||
|
||||
@@ -14,22 +14,22 @@ keywords: team, teams, billing, subscription, payment, plan, plans, cloud, paylo
|
||||

|
||||
_A screenshot of the Team Settings page._
|
||||
|
||||
### Members
|
||||
## Members
|
||||
|
||||
Each team has members that can interact with your projects. You can invite multiple people to your team and each individual can belong to more than one team. You can assign them either `owner` or `user` permissions. Owners are able to make admin-only changes, such as deleting projects, and editing billing information.
|
||||
|
||||
### Adding Members
|
||||
## Adding Members
|
||||
|
||||
To add a new member to your team, visit your Team’s Settings page, and click “Invite Teammate”. You can then add their email address, and assign their role. Press “Save” to send the invitations, which will send an email to the invited team member where they can create a new account.
|
||||
|
||||
### Billing
|
||||
## Billing
|
||||
|
||||
Users can update billing settings and subscriptions for any teams where they are designated as an `owner`. To make updates to the team’s payment methods, visit the Billing page under the Team Settings tab. You can add new cards, delete cards, and set a payment method as a default. The default payment method will be used in the event that another payment method fails.
|
||||
|
||||
### Subscriptions
|
||||
## Subscriptions
|
||||
|
||||
From the Subscriptions page, a team owner can see all current plans for their team. From here, you can see the price of each plan, if there is an active trial, and when you will be billed next.
|
||||
|
||||
### Invoices
|
||||
## Invoices
|
||||
|
||||
The Invoices page will you show you the invoices for your account, as well as the status on their payment.
|
||||
|
||||
@@ -35,7 +35,7 @@ It's often best practice to write your Collections in separate files and then im
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
#### Simple collection example
|
||||
### Simple collection example
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
@@ -58,13 +58,13 @@ export const Orders: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
#### More collection config examples
|
||||
### More collection config examples
|
||||
|
||||
You can find an assortment
|
||||
of [example collection configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/collections) in the Public
|
||||
Demo source code on GitHub.
|
||||
|
||||
### Admin options
|
||||
## Admin options
|
||||
|
||||
You can customize the way that the Admin panel behaves on a collection-by-collection basis by defining the `admin`
|
||||
property on a collection's config.
|
||||
@@ -87,7 +87,7 @@ property on a collection's config.
|
||||
| `listSearchableFields` | Specify which fields should be searched in the List search view. [More](#list-searchable-fields) |
|
||||
| **`pagination`** | Set pagination-specific options for this collection. [More](#pagination) |
|
||||
|
||||
### Preview
|
||||
## Preview
|
||||
|
||||
Collection `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend
|
||||
of your app to preview data.
|
||||
@@ -126,7 +126,7 @@ export const Posts: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Pagination
|
||||
## Pagination
|
||||
|
||||
Here are a few options that you can specify options for pagination on a collection-by-collection basis:
|
||||
|
||||
@@ -135,23 +135,23 @@ Here are a few options that you can specify options for pagination on a collecti
|
||||
| `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. |
|
||||
|
||||
### Access control
|
||||
## Access control
|
||||
|
||||
You can specify extremely granular access control (what users can do with documents in a collection) on a collection by
|
||||
collection basis. To learn more, go to the [Access Control](/docs/access-control/overview) docs.
|
||||
|
||||
### Hooks
|
||||
## Hooks
|
||||
|
||||
Hooks are a powerful way to extend collection functionality and execute your own logic, and can be defined on a
|
||||
collection by collection basis. To learn more, go to the [Hooks](/docs/hooks/overview) documentation.
|
||||
|
||||
### Field types
|
||||
## Field types
|
||||
|
||||
Collections support all field types that Payload has to offer—including simple fields like text and checkboxes all the
|
||||
way to more complicated layout-building field groups like Blocks. [Click here](/docs/fields/overview) to learn more
|
||||
about field types.
|
||||
|
||||
### List Searchable Fields
|
||||
## List Searchable Fields
|
||||
|
||||
In the List view, there is a "search" box that allows you to quickly find a document with a search. By default, it
|
||||
searches on the ID field. If you have `admin.useAsTitle` defined, the list search will use that field. However, you can
|
||||
@@ -169,7 +169,7 @@ those three fields plus the ID field.
|
||||
so your admin queries can remain performant.
|
||||
</Banner>
|
||||
|
||||
### TypeScript
|
||||
## TypeScript
|
||||
|
||||
You can import collection types as follows:
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: config, configuration, documentation, Content Management System, cms,
|
||||
|
||||
Payload utilizes a few Express-specific middleware packages within its own routers. You can customize how they work by passing in configuration options to the main Payload config's `express` property.
|
||||
|
||||
### Custom Middleware
|
||||
## Custom Middleware
|
||||
|
||||
Payload allows you to pass in custom Express middleware to be used on all of the routes it opens. This is useful for adding logging or any other custom functionality to your endpoints.
|
||||
|
||||
@@ -42,7 +42,7 @@ const requestLoggerMiddleware = (req, res, next) => {
|
||||
}
|
||||
```
|
||||
|
||||
### JSON
|
||||
## JSON
|
||||
|
||||
`express.json()` is used to parse JSON body content into JavaScript objects accessible on the Express `req`. Payload allows you to customize all of the `json` method's options. Common examples of customization use-cases are increasing the max allowed JSON body size which defaults to `2MB`.
|
||||
|
||||
@@ -60,7 +60,7 @@ const requestLoggerMiddleware = (req, res, next) => {
|
||||
|
||||
You can find a list of all available options that are able to be passed to `express.json()` [here](https://expressjs.com/en/api.html).
|
||||
|
||||
### Compression
|
||||
## Compression
|
||||
|
||||
Payload uses the `compression` package to optimize transfer size for all of the routes it opens, and you can pass customization options through the Payload config.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ As with Collection configs, it's often best practice to write your Globals in se
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
#### Simple Global example
|
||||
### Simple Global example
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload/types'
|
||||
@@ -58,11 +58,11 @@ const Nav: GlobalConfig = {
|
||||
export default Nav
|
||||
```
|
||||
|
||||
#### Global config example
|
||||
### Global config example
|
||||
|
||||
You can find a few [example Global configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/globals) in the Public Demo source code on GitHub.
|
||||
|
||||
### Admin options
|
||||
## Admin options
|
||||
|
||||
You can customize the way that the Admin panel behaves on a Global-by-Global basis by defining the `admin` property on a Global's config.
|
||||
|
||||
@@ -76,7 +76,7 @@ You can customize the way that the Admin panel behaves on a Global-by-Global bas
|
||||
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
|
||||
| `meta` | Metadata overrides to apply to the [Admin panel](../admin/overview). Included properties are `description` and `openGraph`. |
|
||||
|
||||
### Preview
|
||||
## Preview
|
||||
|
||||
Global `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend of your app to preview data.
|
||||
|
||||
@@ -113,19 +113,19 @@ export const MyGlobal: GlobalConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Access control
|
||||
## Access control
|
||||
|
||||
As with Collections, you can specify extremely granular access control (what users can do with this Global) on a Global-by-Global basis. However, Globals only have `update` and `read` access control due to their nature of only having one document. To learn more, go to the [Access Control](/docs/access-control/overview) docs.
|
||||
|
||||
### Hooks
|
||||
## Hooks
|
||||
|
||||
Globals also fully support a smaller subset of Hooks. To learn more, go to the [Hooks](/docs/hooks/overview) documentation.
|
||||
|
||||
### Field types
|
||||
## Field types
|
||||
|
||||
Globals support all field types that Payload has to offer—including simple fields like text and checkboxes all the way to more complicated layout-building field groups like Blocks. [Click here](/docs/fields/overview) to learn more about field types.
|
||||
|
||||
### TypeScript
|
||||
## TypeScript
|
||||
|
||||
You can import global types as follows:
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ export const Articles: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Admin UI
|
||||
## Admin UI
|
||||
|
||||
The Payload admin panel reads the language settings of a user's browser and display all text in that language, or will fall back to English if the user's language is not yet supported.
|
||||
After a user logs in, they can change their language selection in the `/account` view.
|
||||
@@ -68,13 +68,13 @@ After a user logs in, they can change their language selection in the `/account`
|
||||
[contributions](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md).
|
||||
</Banner>
|
||||
|
||||
### Node Express
|
||||
## Node Express
|
||||
|
||||
Payload's backend uses express middleware to set the language on incoming requests before they are handled. This allows backend validation to return error messages in the user's own language or system generated emails to be sent using the correct translation. You can make HTTP requests with the `accept-language` header and Payload will use that language.
|
||||
|
||||
Anywhere in your Payload app that you have access to the `req` object, you can access payload's extensive internationalization features assigned to `req.i18n`. To access text translations you can use `req.t('namespace:key')`.
|
||||
|
||||
### Configuration Options
|
||||
## Configuration Options
|
||||
|
||||
In your Payload config, you can add translations and customize the settings in `i18n`. Payload will use your custom options and merge it with the default, allowing you to override the settings Payload provides.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ keywords: localization, internationalization, i18n, config, configuration, docum
|
||||
Payload features deep field-based localization support. Maintaining as many locales as you need is easy. All
|
||||
localization support is opt-in by default. To do so, follow the two steps below.
|
||||
|
||||
### Enabling in the Payload config
|
||||
## Enabling in the Payload config
|
||||
|
||||
Add the `localization` property to your Payload config to enable localization project-wide. You'll need to provide a
|
||||
list of all locales that you'd like to support as well as set a few other options.
|
||||
@@ -103,7 +103,7 @@ right-to-left), and `fallbackLocale` property. The locale codes do not need to b
|
||||
to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter
|
||||
language and country codes (ISO 3166‑1) such as `en-US`, `en-UK`, `es-MX`, etc.
|
||||
|
||||
### Locale Properties:
|
||||
## Locale Object Properties
|
||||
|
||||
| Option | Description |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
@@ -125,7 +125,7 @@ Boolean enabling "fallback" locale functionality. If a document is requested in
|
||||
localized value corresponding to the requested locale, then if this property is enabled, the document will automatically
|
||||
fall back to the fallback locale value. If this property is not enabled, the value will not be populated.
|
||||
|
||||
### Field by field localization
|
||||
## Field by field localization
|
||||
|
||||
Payload localization works on a **field** level—not a document level. In addition to configuring the base Payload config
|
||||
to support localization, you need to specify each field that you would like to localize.
|
||||
@@ -166,12 +166,12 @@ and `block`s.
|
||||
strategy.
|
||||
</Banner>
|
||||
|
||||
### Retrieving localized docs
|
||||
## Retrieving localized docs
|
||||
|
||||
When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be
|
||||
used.
|
||||
|
||||
##### REST API
|
||||
#### REST API
|
||||
|
||||
REST API locale functionality relies on URL query parameters.
|
||||
|
||||
@@ -190,7 +190,7 @@ valid locale as provided to your base Payload config, or `'null'`, `'false'`, or
|
||||
fetch('https://localhost:3000/api/pages?locale=es&fallback-locale=none');
|
||||
```
|
||||
|
||||
##### GraphQL API
|
||||
#### GraphQL API
|
||||
|
||||
In the GraphQL API, you can specify `locale` and `fallbackLocale` args to all relevant queries and mutations.
|
||||
|
||||
@@ -218,7 +218,7 @@ query {
|
||||
arguments in nested related document queries.
|
||||
</Banner>
|
||||
|
||||
##### Local API
|
||||
#### Local API
|
||||
|
||||
You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options`
|
||||
argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid
|
||||
|
||||
@@ -49,7 +49,7 @@ Payload is a _config-based_, code-first CMS and application framework. The Paylo
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
#### Simple example
|
||||
### Simple example
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
@@ -106,11 +106,11 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#### Full example config
|
||||
### Full example config
|
||||
|
||||
You can see a full [example config](https://github.com/payloadcms/public-demo/blob/master/src/payload/payload.config.ts) in the Public Demo source code on GitHub.
|
||||
|
||||
### Using environment variables in your config
|
||||
## Using environment variables in your config
|
||||
|
||||
We suggest using the `dotenv` package to handle environment variables alongside of Payload. All that's necessary to do is to require the package as high up in your application as possible (for example, at the top of your `server.js` file), and ensure that it can find an `.env` file that you create.
|
||||
|
||||
@@ -142,7 +142,7 @@ project-name
|
||||
can access it. [Click here](/docs/admin/webpack#admin-environment-vars) for more info.
|
||||
</Banner>
|
||||
|
||||
### Customizing & Automating Config Location Detection
|
||||
## Customizing & Automating Config Location Detection
|
||||
|
||||
Payload is designed to automatically locate your configuration file. By default, it will first look in the root of your current working directory for a file named `payload.config.js` or `payload.config.ts` if you're using TypeScript.
|
||||
|
||||
@@ -152,7 +152,7 @@ In production mode, Payload will first attempt to find the config file in the ou
|
||||
|
||||
Please ensure your `tsconfig.json` is properly configured if you want Payload to accurately auto-detect your configuration file location. If `tsconfig.json` does not exist or doesn't specify `rootDir` or `outDir`, Payload will default to the current working directory.
|
||||
|
||||
#### Overriding the Config Location
|
||||
### Overriding the Config Location
|
||||
|
||||
In addition to the above automated detection, you can specify your own location for the Payload config file. This is done by using the environment variable `PAYLOAD_CONFIG_PATH`. The path you provide via this environment variable can either be absolute or relative to your current working directory. This can be useful in situations where your Payload config is not in a standard location, or you wish to switch between multiple configurations.
|
||||
|
||||
@@ -168,11 +168,11 @@ In addition to the above automated detection, you can specify your own location
|
||||
|
||||
When `PAYLOAD_CONFIG_PATH` is set, Payload will use this path to load the configuration, bypassing all automated detection.
|
||||
|
||||
### Developing within the Config
|
||||
## Developing within the Config
|
||||
|
||||
Payload comes with `isomorphic-fetch` installed which means that even in Node, you can use the `fetch` API just as you would within the browser. No need to import `axios` or similar, unless you want to!
|
||||
|
||||
### TypeScript
|
||||
## TypeScript
|
||||
|
||||
You can import config types as follows:
|
||||
|
||||
@@ -190,7 +190,7 @@ import { SanitizedConfig } from 'payload/config'
|
||||
// Generally, this is only used internally by Payload.
|
||||
```
|
||||
|
||||
### Telemetry
|
||||
## Telemetry
|
||||
|
||||
Payload collects **completely anonymous** telemetry data about general usage. This data is super important to us and helps us accurately understand how we're growing and what we can do to build the software into everything that it can possibly be. The telemetry that we collect also help us demonstrate our growth in an accurate manner, which helps us as we seek investment to build and scale our team. If we can accurately demonstrate our growth, we can more effectively continue to support Payload as free and open-source software. To opt out of telemetry, you can pass `telemetry: false` within your Payload config.
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Ensure you have an npm script called "payload" in your `package.json` file.
|
||||
because Payload should not be globally installed on your system.
|
||||
</Banner>
|
||||
|
||||
### Migration file contents
|
||||
## Migration file contents
|
||||
|
||||
Payload stores all created migrations in a folder that you can specify. By default, migrations are stored
|
||||
in `./src/migrations`.
|
||||
@@ -53,7 +53,7 @@ export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
|
||||
}
|
||||
```
|
||||
|
||||
### Migrations Directory
|
||||
## Migrations Directory
|
||||
|
||||
Each DB adapter has an optional property `migrationDir` where you can override where you want your migrations to be
|
||||
stored/read. If this is not specified, Payload will check the default and possibly make a best effort to find your
|
||||
|
||||
@@ -28,7 +28,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Options
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- |
|
||||
@@ -38,7 +38,7 @@ export default buildConfig({
|
||||
| `migrationDir` | Customize the directory that migrations are stored. |
|
||||
| `transactionOptions` | An object with configuration properties used in [transactions](https://www.mongodb.com/docs/manual/core/transactions/) or `false` which will disable the use of transactions. | |
|
||||
|
||||
### Access to Mongoose models
|
||||
## Access to Mongoose models
|
||||
|
||||
After Payload is initialized, this adapter exposes all of your Mongoose models and they are available for you to work
|
||||
with directly.
|
||||
|
||||
@@ -19,7 +19,7 @@ To use a specific database adapter, you need to install it and configure it acco
|
||||
|
||||
There are several factors to consider when choosing which database technology and hosting option is right for your project and workload. Payload can theoretically support any database, but it's up to you to decide which database to use.
|
||||
|
||||
#### When to use MongoDB
|
||||
### When to use MongoDB
|
||||
|
||||
If your project has a lot of dynamic fields, and you are comfortable with allowing Payload to enforce data integrity across your documents, MongoDB is a great choice. With it, your Payload documents are stored as _one_ document in your database—no matter if you have localization enabled, how many block or array fields you have, etc. This means that the shape of your data in your database will very closely reflect your field schema, and there is minimal complexity involved in storing or retrieving your data.
|
||||
|
||||
@@ -30,7 +30,7 @@ You should prefer MongoDB if:
|
||||
- Most (or everything) in your project is localized
|
||||
- You leverage a lot of array fields, block fields, or `hasMany` select fields and similar
|
||||
|
||||
#### When to use a relational DB
|
||||
### When to use a relational DB
|
||||
|
||||
Many projects might call for more rigid database architecture where the shape of your data is strongly enforced at the database level. For example, if you know the shape of your data and it's relatively "flat", and you don't anticipate it to change often, your workload might suit relational databases like Postgres very well.
|
||||
|
||||
@@ -40,7 +40,7 @@ You should prefer a relational DB like Postgres if:
|
||||
- You require enforced data consistency at the database level
|
||||
- You have a lot of relationships between collections and require relationships to be enforced
|
||||
|
||||
#### Differences in Payload features
|
||||
### Differences in Payload features
|
||||
|
||||
It's important to note that almost everything Payload does is available in all of our officially supported database adapters, including localization, arrays, blocks, etc.
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Options
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
@@ -51,7 +51,7 @@ export default buildConfig({
|
||||
|
||||
|
||||
|
||||
### Access to Drizzle
|
||||
## Access to Drizzle
|
||||
|
||||
After Payload is initialized, this adapter will expose the full power of Drizzle to you for use if you need it.
|
||||
|
||||
@@ -61,7 +61,7 @@ You can access Drizzle as follows:
|
||||
payload.db.drizzle
|
||||
```
|
||||
|
||||
### Tables, relations, and enums
|
||||
## Tables, relations, and enums
|
||||
|
||||
In addition to exposing Drizzle directly, all of the tables, Drizzle relations, and enum configs are exposed for you via the `payload.db` property as well.
|
||||
|
||||
@@ -69,7 +69,7 @@ In addition to exposing Drizzle directly, all of the tables, Drizzle relations,
|
||||
- Enums - `payload.db.enums`
|
||||
- Relations - `payload.db.relations`
|
||||
|
||||
### Prototyping in development mode
|
||||
## Prototyping in development mode
|
||||
|
||||
Drizzle exposes two ways to work locally in development mode.
|
||||
|
||||
@@ -79,7 +79,7 @@ You will be warned if any changes that you make will entail data loss while in d
|
||||
|
||||
Alternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload config.
|
||||
|
||||
### Migration workflows
|
||||
## Migration workflows
|
||||
|
||||
Migrations are extremely powerful thanks to the seamless way that Payload and Drizzle work together. Let's take the following scenario:
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
}
|
||||
```
|
||||
|
||||
### Async Hooks with Transactions
|
||||
## Async Hooks with Transactions
|
||||
|
||||
Since Payload hooks can be async and be written to not await the result, it is possible to have an incorrect success response returned on a request that is rolled back. If you have a hook where you do not `await` the result, then you should **not** pass the `req.transactionID`.
|
||||
|
||||
@@ -59,7 +59,7 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
}
|
||||
```
|
||||
|
||||
### Direct Transaction Access
|
||||
## Direct Transaction Access
|
||||
|
||||
When writing your own scripts or custom endpoints, you may wish to have direct control over transactions. This is useful for interacting with your database outside of Payload's local API.
|
||||
|
||||
|
||||
@@ -6,19 +6,19 @@ desc: Payload uses an adapter pattern to enable email functionality. Set up emai
|
||||
keywords: email, overview, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
### Introduction
|
||||
## Introduction
|
||||
|
||||
Payload has a few email adapters that can be imported to enable email functionality. The [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) package will be the package most will want to install. This package provides an easy way to use [Nodemailer](https://nodemailer.com) for email and won't get in your way for those already familiar.
|
||||
|
||||
The email adapter should be passed into the `email` property of the Payload config. This will allow Payload to send emails for things like password resets, new user verification, and any other email sending needs you may have.
|
||||
|
||||
### Configuration
|
||||
## Configuration
|
||||
|
||||
#### Default Configuration
|
||||
### Default Configuration
|
||||
|
||||
When email is not needed or desired, Payload will log a warning on startup notifying that email is not configured. A warning message will also be logged on any attempt to send an email.
|
||||
|
||||
#### Email Adapter
|
||||
### Email Adapter
|
||||
|
||||
An email adapter will require at least the following fields:
|
||||
|
||||
@@ -28,21 +28,21 @@ An email adapter will require at least the following fields:
|
||||
| **`defaultFromAddress`** \* | The email address part of the From field that will be used when delivering email |
|
||||
|
||||
|
||||
#### Officlal Email Adapters
|
||||
### Official Email Adapters
|
||||
|
||||
| Name | Package | Description |
|
||||
| ---------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Nodemailer | [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) | Use any [Nodemailer transport](https://nodemailer.com/transports), including SMTP, Resend, SendGrid, and more. This was provided by default in Payload 2.x. This is the easiest migration path. |
|
||||
| Resend | [@payloadcms/email-resend](https://www.npmjs.com/package/@payloadcms/email-resend) | Resend email via their REST API. This is preferred for serverless platforms such as Vercel because it is much more lightweight than the nodemailer adapter. |
|
||||
|
||||
### Nodemailer Configuration
|
||||
## Nodemailer Configuration
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`transport`** | The Nodemailer transport object for when you want to do it yourself, not needed when transportOptions is set |
|
||||
| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [Nodemailer documentation](https://nodemailer.com) or see the examples below |
|
||||
|
||||
### Use SMTP
|
||||
## Use SMTP
|
||||
|
||||
Simple Mail Transfer Protocol (SMTP) options can be passed in using the `transportOptions` object on the `email` options. See the [Nodemailer SMTP documentation](https://nodemailer.com/smtp/) for more information, including details on when `secure` should and should not be set to `true`.
|
||||
|
||||
@@ -127,7 +127,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Resend Configuration
|
||||
## Resend Configuration
|
||||
|
||||
The Resend adapter requires an API key to be passed in the options. This can be found in the Resend dashboard. This is the preferred package if you are deploying on Vercel because this is much more lightweight than the Nodemailer adapter.
|
||||
|
||||
@@ -148,7 +148,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Sending Mail
|
||||
## Sending Mail
|
||||
|
||||
With a working transport you can call it anywhere you have access to payload by calling `payload.sendEmail(message)`. The `message` will contain the `to`, `subject` and `html` or `text` for the email being sent. Other options are also available and can be seen in the sendEmail args. Support for these will depend on the adapter being used.
|
||||
|
||||
@@ -161,6 +161,6 @@ const email = await payload.sendEmail({
|
||||
})
|
||||
```
|
||||
|
||||
### Using multiple mail providers
|
||||
## Using multiple mail providers
|
||||
|
||||
Payload supports the use of a single transporter of email, but there is nothing stopping you from having more. Consider a use case where sending bulk email is handled differently than transactional email and could be done using a [hook](/docs/hooks/overview).
|
||||
|
||||
@@ -24,7 +24,7 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
|
||||
- Navigational structures where editors can specify nav items containing pages ([relationship field](/docs/fields/relationship)), an "open in new tab" [checkbox field](/docs/fields/checkbox)
|
||||
- Event agenda "timeslots" where you need to specify start & end time ([date field](/docs/fields/date)), label ([text field](/docs/fields/text)), and Learn More page [relationship](/docs/fields/relationship)
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -49,7 +49,7 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin Config
|
||||
## Admin Config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
|
||||
|
||||
@@ -59,7 +59,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
| **`components.RowLabel`** | Function or React component to be rendered as the label on the array row. Receives `({ data, index, path })` as args |
|
||||
| **`isSortable`** | Disable order sorting by setting this value to `false` |
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ keywords: blocks, fields, config, configuration, documentation, Content Manageme
|
||||
- A form builder tool where available block configs might be `Text`, `Select`, or `Checkbox`.
|
||||
- Virtual event agenda "timeslots" where a timeslot could either be a `Break`, a `Presentation`, or a `BreakoutSession`.
|
||||
|
||||
### Field config
|
||||
## Field config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -49,7 +49,7 @@ keywords: blocks, fields, config, configuration, documentation, Content Manageme
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin Config
|
||||
## Admin Config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
|
||||
|
||||
@@ -58,7 +58,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
| **`initCollapsed`** | Set the initial collapsed state |
|
||||
| **`isSortable`** | Disable order sorting by setting this value to `false` |
|
||||
|
||||
### Block configs
|
||||
## Block configs
|
||||
|
||||
Blocks are defined as separate configs of their own.
|
||||
|
||||
@@ -84,7 +84,7 @@ Blocks are defined as separate configs of their own.
|
||||
| **`dbName`** | Custom table name for this block type when using SQL database adapter ([Postgres](/docs/database/postgres)). Auto-generated from slug if not defined.
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
#### Auto-generated data per block
|
||||
### Auto-generated data per block
|
||||
|
||||
In addition to the field data that you define on each block, Payload will store two additional properties on each block:
|
||||
|
||||
@@ -96,7 +96,7 @@ The `blockType` is saved as the slug of the block that has been selected.
|
||||
|
||||
The Admin panel provides each block with a `blockName` field which optionally allows editors to label their blocks for better editability and readability.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.js`
|
||||
|
||||
@@ -139,7 +139,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
## 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:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ keywords: checkbox, fields, config, configuration, documentation, Content Manage
|
||||
caption="Admin panel screenshot of Checkbox field with Text field below"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -35,7 +35,7 @@ keywords: checkbox, fields, config, configuration, documentation, Content Manage
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ keywords: code, fields, config, configuration, documentation, Content Management
|
||||
|
||||
This field uses the `monaco-react` editor syntax highlighting.
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -44,7 +44,7 @@ This field uses the `monaco-react` editor syntax highlighting.
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin Config
|
||||
## Admin Config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
|
||||
|
||||
@@ -53,7 +53,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
| **`language`** | This property can be set to any language listed [here](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages). |
|
||||
| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IDiffEditorConstructionOptions.html). |
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ keywords: row, fields, config, configuration, documentation, Content Management
|
||||
caption="Admin panel screenshot of a Collapsible field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -29,7 +29,7 @@ keywords: row, fields, config, configuration, documentation, Content Management
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin Config
|
||||
## Admin Config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
|
||||
|
||||
@@ -37,7 +37,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
| ------------------- | ------------------------------- |
|
||||
| **`initCollapsed`** | Set the initial collapsed state |
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ keywords: date, fields, config, configuration, documentation, Content Management
|
||||
|
||||
This field uses [`react-datepicker`](https://www.npmjs.com/package/react-datepicker) for the Admin panel component.
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -40,7 +40,7 @@ This field uses [`react-datepicker`](https://www.npmjs.com/package/react-datepic
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin Config
|
||||
## Admin Config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can customize the following fields that will adjust how the component displays in the admin panel via the `date` property.
|
||||
|
||||
@@ -61,7 +61,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
|
||||
_\* This property is passed directly to [react-datepicker](https://github.com/Hacker0x01/react-datepicker/blob/master/docs/datepicker.md). ._
|
||||
|
||||
#### Display Format and Picker Appearance
|
||||
### Display Format and Picker Appearance
|
||||
|
||||
These properties only affect how the date is displayed in the UI. The full date is always stored in the format `YYYY-MM-DDTHH:mm:ss.SSSZ` (e.g. `1999-01-01T8:00:00.000+05:00`).
|
||||
|
||||
@@ -71,7 +71,7 @@ These properties only affect how the date is displayed in the UI. The full date
|
||||
|
||||
When only `pickerAppearance` is set, an equivalent format will be rendered in the date field cell. To overwrite this format, set `displayFormat`.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ keywords: email, fields, config, configuration, documentation, Content Managemen
|
||||
caption="Admin panel screenshot of an Email field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -36,7 +36,7 @@ keywords: email, fields, config, configuration, documentation, Content Managemen
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), this field type allows for the following `admin` properties:
|
||||
|
||||
@@ -48,7 +48,7 @@ Set this property to define a placeholder string for the field.
|
||||
|
||||
Set this property to a string that will be used for browser autocomplete.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ keywords: group, fields, config, configuration, documentation, Content Managemen
|
||||
caption="Admin panel screenshot of a Group field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -38,7 +38,7 @@ keywords: group, fields, config, configuration, documentation, Content Managemen
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Group allows for the following admin property:
|
||||
|
||||
@@ -46,7 +46,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
|
||||
Set this property to `true` to hide this field's gutter within the admin panel. The field gutter is rendered as a vertical line and padding, but often if this field is nested within a Group, Block, or Array, you may want to hide the gutter.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ keywords: json, jsonSchema, schema, validation, fields, config, configuration, d
|
||||
|
||||
This field uses the `monaco-react` editor syntax highlighting.
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -43,7 +43,7 @@ This field uses the `monaco-react` editor syntax highlighting.
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin Config
|
||||
## Admin Config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
|
||||
|
||||
@@ -51,7 +51,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/typedoc/variables/editor.EditorOptions.html). |
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
@@ -69,14 +69,14 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
}
|
||||
```
|
||||
### JSON Schema Validation
|
||||
## JSON Schema Validation
|
||||
|
||||
Payload JSON fields fully support the [JSON schema](https://json-schema.org/) standard. By providing a schema in your field config, the editor will be guided in the admin UI, getting typeahead for properties and their formats automatically. When the document is saved, the default validation will prevent saving any invalid data in the field according to the schema in your config.
|
||||
|
||||
If you only provide a URL to a schema, Payload will fetch the desired schema if it is publicly available. If not, it is recommended to add the schema directly to your config or import it from another file so that it can be implemented consistently in your project.
|
||||
|
||||
|
||||
#### Local JSON Schema
|
||||
### Local JSON Schema
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
@@ -109,7 +109,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
// Attempting to create {"foo": "not-bar"} will throw an error
|
||||
```
|
||||
|
||||
#### Remote JSON Schema
|
||||
### Remote JSON Schema
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ keywords: number, fields, config, configuration, documentation, Content Manageme
|
||||
caption="Admin panel screenshot of a Number field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
@@ -44,7 +44,7 @@ keywords: number, fields, config, configuration, documentation, Content Manageme
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), this field type allows for the following `admin` properties:
|
||||
|
||||
@@ -60,7 +60,7 @@ Set this property to define a placeholder string for the field.
|
||||
|
||||
Set this property to a string that will be used for browser autocomplete.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export const Page: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Field types
|
||||
## Field types
|
||||
|
||||
- [Array](/docs/fields/array) - for repeating content, supports nested fields
|
||||
- [Blocks](/docs/fields/blocks) - block-based fields, allowing powerful layout creation
|
||||
@@ -60,19 +60,19 @@ export const Page: CollectionConfig = {
|
||||
- [Upload](/docs/fields/upload) - allows local file and image upload
|
||||
- [UI](/docs/fields/ui) - inject your own custom components and do whatever you need
|
||||
|
||||
### Field-level hooks
|
||||
## Field-level hooks
|
||||
|
||||
One of the most powerful parts about Payload is its ability for you to define field-level hooks that can control the logic of your fields to a fine-grained level. for more information about how to define field hooks, [click here](/docs/hooks/overview#field-hooks).
|
||||
|
||||
### Field-level access control
|
||||
## Field-level access control
|
||||
|
||||
In addition to being able to define access control on a document-level, you can define extremely granular permissions on a field by field level. For more information about field-level access control, [click here](/docs/access-control/overview#fields).
|
||||
|
||||
### Field names
|
||||
## Field names
|
||||
|
||||
Some fields use their `name` property as a unique identifier to store and retrieve from the database. `__v`, `salt`, and `hash` are all reserved field names which are sanitized from Payload's config and cannot be used.
|
||||
|
||||
### Validation
|
||||
## Validation
|
||||
|
||||
Field validation is enforced automatically based on the field type and other properties such as `required` or `min` and `max` value constraints on certain field types. This default behavior can be replaced by providing your own validate function for any field. It will be used on both the frontend and the backend, so it should not rely on any Node-specific packages. The validation function can be either synchronous or asynchronous and expects to return either `true` or a string error message to display in both API responses and within the Admin panel.
|
||||
|
||||
@@ -91,7 +91,7 @@ There are two arguments available to custom validation functions.
|
||||
| `user` | An object containing the currently authenticated user |
|
||||
| `payload` | If the `validate` function is being executed on the server, Payload will be exposed for easily running local operations. |
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
@@ -140,7 +140,7 @@ const field: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
### Customizable ID
|
||||
## Customizable ID
|
||||
|
||||
Collections ID fields are generated automatically by default. An explicit `id` field can be declared in the `fields` array to override this behavior.
|
||||
Users are then required to provide a custom ID value when creating a record through the Admin UI or API.
|
||||
@@ -159,7 +159,7 @@ Example:
|
||||
}
|
||||
```
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to each field's base configuration, you can define specific traits and properties for fields that only have effect on how they are rendered in the Admin panel. The following properties are available for all fields within the `admin` property:
|
||||
|
||||
@@ -179,11 +179,11 @@ In addition to each field's base configuration, you can define specific traits a
|
||||
| `disableListFilter` | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |
|
||||
| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. |
|
||||
|
||||
### Custom components
|
||||
## Custom components
|
||||
|
||||
All Payload fields support the ability to swap in your own React components with ease. For more information, including examples, [click here](/docs/admin/components#fields).
|
||||
|
||||
### Conditional logic
|
||||
## 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:
|
||||
|
||||
@@ -222,7 +222,7 @@ The `condition` function should return a boolean that will control if the field
|
||||
}
|
||||
```
|
||||
|
||||
### Default values
|
||||
## Default values
|
||||
|
||||
Fields can be prefilled with starting values using the `defaultValue` property. This is used in the admin UI and also on the backend as API requests will be populated with missing or undefined field values. You can assign the defaultValue directly in the field configuration or supply a function for dynamic behavior. Values assigned during a create request on the server are added before validation occurs.
|
||||
|
||||
@@ -252,7 +252,7 @@ const field = {
|
||||
You can use async defaultValue functions to fill fields with data from API requests.
|
||||
</Banner>
|
||||
|
||||
### Description
|
||||
## Description
|
||||
|
||||
A description can be configured in three ways.
|
||||
|
||||
@@ -314,7 +314,7 @@ This example will display the number of characters allowed as the user types.
|
||||
|
||||
This component will count the number of characters entered, as well as display the path of the field.
|
||||
|
||||
### TypeScript
|
||||
## TypeScript
|
||||
|
||||
You can import the internal Payload `Field` type as well as other common field types as follows:
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ keywords: point, geolocation, geospatial, geojson, 2dsphere, config, configurati
|
||||
|
||||
The data structure in the database matches the GeoJSON structure to represent point. The Payload APIs simplifies the object data to only the [longitude, latitude] location.
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -50,7 +50,7 @@ _\* An asterisk denotes that a property is required._
|
||||
<strong>Note:</strong> The Point field type is currently only supported in MongoDB.
|
||||
</Banner>
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
@@ -69,6 +69,6 @@ export const ExampleCollection: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Querying
|
||||
## Querying
|
||||
|
||||
In order to do query based on the distance to another point, you can use the `near` operator. When querying using the near operator, the returned documents will be sorted by nearest first.
|
||||
|
||||
@@ -18,7 +18,7 @@ keywords: radio, fields, config, configuration, documentation, Content Managemen
|
||||
caption="Admin panel screenshot of a Radio field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -49,7 +49,7 @@ _\* An asterisk denotes that a property is required._
|
||||
being used as a GraphQL enum.
|
||||
</Banner>
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Radio Group field type allows for the specification of the following `admin` properties:
|
||||
|
||||
@@ -57,7 +57,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
|
||||
|
||||
The `layout` property allows for the radio group to be styled as a horizonally or vertically distributed list. The default value is `horizontal`.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ keywords: relationship, fields, config, configuration, documentation, Content Ma
|
||||
- To allow for an `Order` to feature a `placedBy` relationship to either an `Organization` or `User` collection
|
||||
- To assign `Category` documents to `Post` documents
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
@@ -58,7 +58,7 @@ _\* An asterisk denotes that a property is required._
|
||||
related documents that are returned by the API.
|
||||
</Banner>
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Relationship field type also
|
||||
allows for the following admin-specific properties:
|
||||
@@ -122,7 +122,7 @@ In this configuration:
|
||||
|
||||
Note: If `sortOptions` is not defined, the default sorting behavior of the Relationship field dropdown will be used.
|
||||
|
||||
### Filtering relationship options
|
||||
## Filtering relationship options
|
||||
|
||||
Options can be dynamically limited by supplying a [query constraint](/docs/queries/overview), which will be used both
|
||||
for validating input and filtering available relationships in the UI.
|
||||
@@ -139,7 +139,7 @@ called with an argument object with the following properties:
|
||||
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation |
|
||||
| `user` | An object containing the currently authenticated user |
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
@@ -181,13 +181,13 @@ You can learn more about writing queries [here](/docs/queries/overview).
|
||||
<strong>payload/fields/validations</strong> in your validate function.
|
||||
</Banner>
|
||||
|
||||
### How the data is saved
|
||||
## How the data is saved
|
||||
|
||||
Given the variety of options possible within the `relationship` field type, the shape of the data needed for creating
|
||||
and updating these fields can vary. The following sections will describe the variety of data shapes that can arise from
|
||||
this field.
|
||||
|
||||
#### Has One
|
||||
### Has One
|
||||
|
||||
The most simple pattern of a relationship is to use `hasMany: false` with a `relationTo` that allows for only one type
|
||||
of collection.
|
||||
@@ -221,7 +221,7 @@ When querying documents in this collection via REST API, you could query as foll
|
||||
|
||||
`?where[owner][equals]=6031ac9e1289176380734024`.
|
||||
|
||||
#### Has One - Polymorphic
|
||||
### Has One - Polymorphic
|
||||
|
||||
Also known as **dynamic references**, in this configuration, the `relationTo` field is an array of Collection slugs that
|
||||
tells Payload which Collections are valid to reference.
|
||||
@@ -263,7 +263,7 @@ You can also query for documents where a field has a relationship to a specific
|
||||
|
||||
This query would return only documents that have an owner relationship to organizations.
|
||||
|
||||
#### Has Many
|
||||
### Has Many
|
||||
|
||||
The `hasMany` tells Payload that there may be more than one collection saved to the field.
|
||||
|
||||
@@ -295,7 +295,7 @@ When querying documents, the format does not change for arrays:
|
||||
|
||||
`?where[owners][equals]=6031ac9e1289176380734024`.
|
||||
|
||||
#### Has Many - Polymorphic
|
||||
### Has Many - Polymorphic
|
||||
|
||||
```ts
|
||||
{
|
||||
@@ -336,7 +336,7 @@ Querying is done in the same way as the earlier Polymorphic example:
|
||||
|
||||
`?where[owners.value][equals]=6031ac9e1289176380734024`.
|
||||
|
||||
#### Querying and Filtering Polymorphic Relationships
|
||||
### Querying and Filtering Polymorphic Relationships
|
||||
|
||||
Polymorphic and non-polymorphic relationships must be queried differently because of how the related data is stored and
|
||||
may be inconsistent across different collections. Because of this, filtering polymorphic relationship fields from the
|
||||
|
||||
@@ -30,12 +30,13 @@ Right now, Payload is officially supporting two rich text editors:
|
||||
Consistent with Payload's goal of making you learn as little of Payload as possible, customizing
|
||||
and using the Rich Text Editor does not involve learning how to develop for a <em>Payload</em>{' '}
|
||||
rich text editor.
|
||||
</strong>{' '}
|
||||
</strong>
|
||||
|
||||
Instead, you can invest your time and effort into learning the underlying open-source tools that
|
||||
will allow you to apply your learnings elsewhere as well.
|
||||
</Banner>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -55,7 +56,7 @@ Right now, Payload is officially supporting two rich text editors:
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Rich Text editor allows for the following admin properties:
|
||||
|
||||
@@ -71,6 +72,6 @@ Set this property to `true` to hide this field's gutter within the admin panel.
|
||||
|
||||
Override the default text direction of the Admin panel for this field. Set to `true` to force right-to-left text direction.
|
||||
|
||||
### 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 either the [Slate docs](/docs/rich-text/slate) or the [Lexical docs](/docs/rich-text/lexical) depending on which editor you're using.
|
||||
|
||||
@@ -18,7 +18,7 @@ keywords: row, fields, config, configuration, documentation, Content Management
|
||||
caption="Admin panel screenshot of a Row field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -28,7 +28,7 @@ keywords: row, fields, config, configuration, documentation, Content Management
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ keywords: select, multi-select, fields, config, configuration, documentation, Co
|
||||
caption="Admin panel screenshot of a Select field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -52,7 +52,7 @@ _\* An asterisk denotes that a property is required._
|
||||
being used as a GraphQL enum.
|
||||
</Banner>
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Select field type also allows for the following admin-specific properties:
|
||||
|
||||
@@ -64,7 +64,7 @@ Set to `true` if you'd like this field to be clearable within the Admin UI.
|
||||
|
||||
Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop. (Only works when `hasMany` is set to `true`)
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
@@ -101,7 +101,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Customization
|
||||
## Customization
|
||||
|
||||
The Select field UI component can be customized by providing a custom React component to the `components` object in the Base config.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ keywords: tabs, fields, config, configuration, documentation, Content Management
|
||||
caption="Tabs field type used to separate Hero fields from Page Layout"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
@@ -27,7 +27,7 @@ keywords: tabs, fields, config, configuration, documentation, Content Management
|
||||
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
#### Tab-specific Config
|
||||
### Tab-specific Config
|
||||
|
||||
Each tab must have either a `name` or `label` and the required `fields` array. You can also optionally pass a `description` to render within each individual tab.
|
||||
|
||||
@@ -41,7 +41,7 @@ Each tab must have either a `name` or `label` and the required `fields` array. Y
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ keywords: text, fields, config, configuration, documentation, Content Management
|
||||
caption="Admin panel screenshot of a Text field and read-only Text field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -44,7 +44,7 @@ keywords: text, fields, config, configuration, documentation, Content Management
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Text field type allows for the following `admin` properties:
|
||||
|
||||
@@ -60,7 +60,7 @@ Set this property to a string that will be used for browser autocomplete.
|
||||
|
||||
Override the default text direction of the Admin panel for this field. Set to `true` to force right-to-left text direction.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ keywords: textarea, fields, config, configuration, documentation, Content Manage
|
||||
caption="Admin panel screenshot of a Textarea field and read-only Textarea field"
|
||||
/>
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -41,7 +41,7 @@ keywords: textarea, fields, config, configuration, documentation, Content Manage
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin config
|
||||
## Admin config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Textarea field type allows for the following `admin` properties:
|
||||
|
||||
@@ -57,7 +57,7 @@ Set this property to a string that will be used for browser autocomplete.
|
||||
|
||||
Override the default text direction of the Admin panel for this field. Set to `true` to force right-to-left text direction.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ With this field, you can also inject custom `Cell` components that appear as add
|
||||
- Add a "view page" button into a Pages List view to give editors a shortcut to view a page on the frontend of the site
|
||||
- Build a "clear cache" button or similar mechanism to manually clear caches of specific documents
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -36,7 +36,7 @@ With this field, you can also inject custom `Cell` components that appear as add
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ keywords: upload, images media, fields, config, configuration, documentation, Co
|
||||
- To allow for a `Product` to deliver a downloadable asset like PDF or MP3
|
||||
- To give a layout building block the ability to feature a background image
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -56,7 +56,7 @@ keywords: upload, images media, fields, config, configuration, documentation, Co
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
@@ -76,7 +76,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Filtering upload options
|
||||
## Filtering upload options
|
||||
|
||||
Options can be dynamically limited by supplying a [query constraint](/docs/queries/overview), which will be used both
|
||||
for validating input and filtering available uploads in the UI.
|
||||
@@ -93,7 +93,7 @@ called with an argument object with the following properties:
|
||||
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation |
|
||||
| `user` | An object containing the currently authenticated user |
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
```ts
|
||||
const uploadField = {
|
||||
|
||||
@@ -8,13 +8,13 @@ keywords: documentation, getting started, guide, Content Management System, cms,
|
||||
|
||||
Payload is based around a small and intuitive set of concepts. Before starting to work with Payload, it's a good idea to familiarize yourself with the following:
|
||||
|
||||
### Config
|
||||
## Config
|
||||
|
||||
<Banner type="info">The Payload config is where you configure everything that Payload does.</Banner>
|
||||
|
||||
By default, the Payload config lives in the root folder of your code and is named `payload.config.js` (`payload.config.ts` if you're using TypeScript), but you can customize its name and where you store it. You can write full functions and even full React components right into your config.
|
||||
|
||||
### Collections
|
||||
## Collections
|
||||
|
||||
<Banner type="info">
|
||||
A Collection represents a type of content that Payload will store and can contain many documents.
|
||||
@@ -24,7 +24,7 @@ Collections define the shape of your data as well as all functionalities attache
|
||||
|
||||
They can represent anything you can store in a database - for example - pages, posts, users, people, orders, categories, events, customers, transactions, and anything else your app needs.
|
||||
|
||||
### Globals
|
||||
## Globals
|
||||
|
||||
<Banner type="info">
|
||||
A Global is a "one-off" piece of content that is perfect for storing navigational structures,
|
||||
@@ -33,7 +33,7 @@ They can represent anything you can store in a database - for example - pages, p
|
||||
|
||||
Globals are in many ways similar to Collections, but there is only ever **one** instance of a Global, whereas Collections can contain many documents.
|
||||
|
||||
### Fields
|
||||
## Fields
|
||||
|
||||
<Banner type="info">
|
||||
Fields are the building blocks of Payload. Collections and Globals both use Fields to define the
|
||||
@@ -42,7 +42,7 @@ Globals are in many ways similar to Collections, but there is only ever **one**
|
||||
|
||||
Payload comes with [many different field types](../fields/overview) that give you a ton of flexibility while designing your API. Each Field type has its own potential properties that allow you to customize how they work.
|
||||
|
||||
### Hooks
|
||||
## Hooks
|
||||
|
||||
<Banner type="info">
|
||||
Hooks are where you can "tie in" to existing Payload actions to perform your own additional logic
|
||||
@@ -53,7 +53,7 @@ Hooks are an extremely powerful concept and are central to extending and customi
|
||||
|
||||
There are many more potential reasons to use Hooks. For more, visit the [Hooks documentation](/docs/hooks/overview).
|
||||
|
||||
### Access Control
|
||||
## Access Control
|
||||
|
||||
<Banner type="info">
|
||||
Access Control refers to Payload's system of defining who can do what to your API.
|
||||
@@ -63,7 +63,7 @@ Access Control is extremely powerful but easy and intuitive to manage. You can e
|
||||
|
||||
For more, visit the [Access Control documentation](/docs/access-control/overview).
|
||||
|
||||
### Depth
|
||||
## Depth
|
||||
|
||||
<Banner type="info">
|
||||
"Depth" gives you control over how many levels down related documents should be automatically
|
||||
@@ -156,7 +156,7 @@ To populate `user.author.department` in it's entirety you could specify `?depth=
|
||||
}
|
||||
```
|
||||
|
||||
#### Field-level max depth
|
||||
### Field-level max depth
|
||||
|
||||
Fields like relationships or uploads can have a `maxDepth` property that limits the depth of the population for that field. Here are some examples:
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: To quickly get started with Payload, simply run npx create-payload-app or
|
||||
keywords: documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
#### Software Requirements
|
||||
## Software Requirements
|
||||
|
||||
Payload requires the following software:
|
||||
|
||||
@@ -48,7 +48,7 @@ Write the above code into your newly created config file. This baseline config w
|
||||
|
||||
Although this is just the bare minimum config, there are _many_ more options that you can control here. To reference the full config and all of its options, [click here](/docs/configuration/overview).
|
||||
|
||||
### Server
|
||||
## Server
|
||||
|
||||
Now that you've got a baseline Payload config, it's time to initialize Payload. It requires an Express server that you provide, so if you're not familiar with how to set up a baseline Express server, please read up on exactly what Express is and why to use it. Express' own [Documentation](https://expressjs.com/en/starter/hello-world.html) is a good place to start. Otherwise, follow along below for how to build your own Express server to use with Payload.
|
||||
|
||||
@@ -100,54 +100,54 @@ PAYLOAD_SECRET=your-payload-secret
|
||||
|
||||
Here is a list of all properties available to pass through `payload.init`:
|
||||
|
||||
##### secret
|
||||
#### secret
|
||||
|
||||
**Required**. This is a secure string that will be used to authenticate with Payload. It can be random but should be at least 14 characters and be very difficult to guess.
|
||||
|
||||
Payload uses this secret key to generate secure user tokens (JWT). Behind the scenes, we do not use your secret key to encrypt directly - instead, we first take the secret key and create an encrypted string using the SHA-256 hash function. Then, we reduce the encrypted string to its first 32 characters. This final value is what Payload uses for encryption.
|
||||
|
||||
##### config
|
||||
#### config
|
||||
|
||||
Allows you to pass your config directly to the onInit function. The config passed here should match the payload.config file.
|
||||
|
||||
##### disableOnInit
|
||||
#### disableOnInit
|
||||
|
||||
A boolean that disables running your `onInit` function when Payload starts up.
|
||||
|
||||
##### disableDBConnect
|
||||
#### disableDBConnect
|
||||
|
||||
A boolean that disables the database connection when Payload starts up.
|
||||
|
||||
##### email
|
||||
#### email
|
||||
|
||||
An object used to configure SMTP. [Read more](/docs/email/overview).
|
||||
|
||||
##### express
|
||||
#### express
|
||||
|
||||
This is your Express app as shown above. Payload will tie into your existing `app` and scope all of its functionalities to sub-routers. By default, Payload will add an `/admin` router and an `/api` router, but you can customize these paths.
|
||||
|
||||
##### local
|
||||
#### local
|
||||
|
||||
A boolean that when set to `true` tells Payload to start in local-only mode which will bypass setting up API routes. When set to `true`, `express` is not required. This is useful when running scripts that need to use Payload's [local-api](/docs/local-api/overview).
|
||||
|
||||
##### loggerDestination
|
||||
#### loggerDestination
|
||||
|
||||
Specify destination stream for the built-in Pino logger that Payload uses for internal logging. See [Pino Docs](https://getpino.io/#/docs/api?id=pino-destination) for more info on what is available.
|
||||
|
||||
##### loggerOptions
|
||||
#### loggerOptions
|
||||
|
||||
Specify options for the built-in Pino logger that Payload uses for internal logging. See [Pino Docs](https://getpino.io/#/docs/api?id=options) for more info on what is available.
|
||||
|
||||
##### onInit
|
||||
#### onInit
|
||||
|
||||
A function that is called immediately following startup that receives the Payload instance as it's only argument.
|
||||
|
||||
### Test it out
|
||||
## Test it out
|
||||
|
||||
After you've gotten this far, it's time to boot up Payload. Start your project in your application's folder to get going.
|
||||
|
||||
After it starts, you can go to `http://localhost:3000/admin` to create your first Payload user!
|
||||
|
||||
### Docker
|
||||
## Docker
|
||||
|
||||
Looking to deploy Payload with Docker? New projects with `create-payload-app` come with a Dockerfile and docker-compose.yml file ready to go. Examples of these files can be seen in our [Deployment docs](/docs/production/deployment#docker).
|
||||
|
||||
@@ -38,7 +38,7 @@ In this way, the CMS can ensure that its content editing experience is highly po
|
||||
|
||||
At this point this concept is [widely](https://en.wikipedia.org/wiki/Headless_content_management_system) [discussed](https://css-tricks.com/what-is-a-headless-cms/) online, and for good reason. The web has become more complicated and with complexity comes the demand for developers to better structure their code. The rise of interface libraries like React and Vue are now the de-facto standard for building modern applications and traditional content management systems are often not designed to make use of them.
|
||||
|
||||
### Why Payload?
|
||||
## Why Payload?
|
||||
|
||||
The team behind Payload has been building websites and apps with existing content management systems and application frameworks for over a decade. We know what works and what doesn't about each of the existing solutions, and to this day have found no silver bullet solution.
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ This is Payload's GraphQL dependency. You should not install your own copy of Gr
|
||||
|
||||
This is a copy of the currently running Payload instance, which provides you with existing GraphQL types for all of your Collections and Globals - among other things.
|
||||
|
||||
##### Return value
|
||||
## Return value
|
||||
|
||||
Both `graphQL.queries` and `graphQL.mutations` functions should return an object with properties equal to your newly written GraphQL queries and mutations.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
`payload.config.js`:
|
||||
|
||||
@@ -68,7 +68,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Resolver function
|
||||
## Resolver function
|
||||
|
||||
In your resolver, make sure you set `depth: 0` if you're returning data directly from the local API so that GraphQL can correctly resolve queries to nested values such as relationship data.
|
||||
|
||||
@@ -96,7 +96,7 @@ An object containing the `req` and `res` objects that will provide you with the
|
||||
|
||||
Contextual information about the currently running GraphQL operation. You can get schema information from this as well as contextual information about where this resolver function is being run.
|
||||
|
||||
### Types
|
||||
## Types
|
||||
|
||||
We've exposed a few types and utilities to help you extend the API further. Payload uses the GraphQL.js package for which you can view the full list of available types in the [official documentation](https://graphql.org/graphql-js/type/).
|
||||
|
||||
@@ -138,7 +138,7 @@ graphQL?: {
|
||||
}
|
||||
```
|
||||
|
||||
### Best practices
|
||||
## Best practices
|
||||
|
||||
There are a few ways to structure your code, we recommend using a dedicated `graphql` directory so you can keep all of your logic in one place. You have total freedom of how you want to structure this but a common pattern is to group functions by type and with their resolver.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: headless cms, typescript, documentation, Content Management System, cm
|
||||
|
||||
When working with GraphQL it is useful to have the schema for development of other projects that need to call on your GraphQL endpoint. In Payload the schema is controlled by your collections and globals and is made available to the developer or third parties, it is not necessary for developers using Payload to write schema types manually.
|
||||
|
||||
### Schema generation script
|
||||
## Schema generation script
|
||||
|
||||
Run the following command in a Payload project to generate your project's GraphQL schema from Payload:
|
||||
|
||||
@@ -16,7 +16,7 @@ Run the following command in a Payload project to generate your project's GraphQ
|
||||
payload generate:graphQLSchema
|
||||
```
|
||||
|
||||
### Custom Field Schemas
|
||||
## Custom Field Schemas
|
||||
|
||||
For `array`, `block`, `group` and named `tab` fields, you can generate top level reusable interfaces. The following group field config:
|
||||
|
||||
@@ -56,7 +56,7 @@ type Collection1 {
|
||||
|
||||
The above example outputs all your definitions to a file relative from your payload config as `./graphql/schema.graphql`. By default, the file will be output to your current working directory as `schema.graphql`.
|
||||
|
||||
#### Adding an NPM script
|
||||
### Adding an NPM script
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important</strong>
|
||||
|
||||
@@ -28,13 +28,13 @@ Example uses:
|
||||
|
||||
There are many more use cases for Hooks and the sky is the limit.
|
||||
|
||||
#### Async vs. synchronous
|
||||
## Async vs. synchronous
|
||||
|
||||
All hooks can be written as either synchronous or asynchronous functions. If the Hook should modify data before a document is updated or created, and it relies on asynchronous actions such as fetching data from a third party, it might make sense to define your Hook as an asynchronous function, so you can be sure that your Hook completes before the operation's lifecycle continues. Async hooks are run in series - so if you have two async hooks defined, the second hook will wait for the first to complete before it starts.
|
||||
|
||||
If your Hook simply performs a side-effect, such as updating a CRM, it might be okay to define it synchronously, so the Payload operation does not have to wait for your hook to complete.
|
||||
|
||||
#### Server-only execution
|
||||
## Server-only execution
|
||||
|
||||
Payload Hooks are only triggered on the server. You can safely [remove your hooks](/docs/admin/webpack#aliasing-server-only-modules) from your Admin panel's client-side code by customizing the Webpack config, which not only keeps your Admin bundles' filesize small but also ensures that any server-side only code does not cause problems within browser environments.
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ keywords: vercel, vercel content link, visual editing, content source maps, Cont
|
||||
team](https://payloadcms.com/for-enterprise) for help with your integration.
|
||||
</Banner>
|
||||
|
||||
### How it works
|
||||
## How it works
|
||||
|
||||
To power Vercel Content Link, Payload embeds Content Source Maps into its API responses. Content Source Maps are invisible, encoded JSON values that include a link back to the field in the CMS that generated the content. When rendered on the page, Vercel detects and decodes these values to display the Content Link interface.
|
||||
|
||||
For full details on how the encoding and decoding algorithm works, check out [`@vercel/stega`](https://www.npmjs.com/package/@vercel/stega).
|
||||
|
||||
### Getting Started
|
||||
## Getting Started
|
||||
|
||||
Setting up Payload with Vercel Content Link is easy. First, install the `@payloadcms/plugin-csm` plugin into your project. This plugin requires an API key to install, [contact our sales team](https://payloadcms.com/for-enterprise) if you don't already have one.
|
||||
|
||||
@@ -74,15 +74,15 @@ if (isDraftMode || process.env.VERCEL_ENV === 'preview') {
|
||||
|
||||
And that's it! You are now ready to enter Edit Mode and begin visually editing your content.
|
||||
|
||||
##### Edit Mode
|
||||
#### Edit Mode
|
||||
|
||||
To see Content Link on your site, you first need to visit any preview deployment on Vercel and login using the Vercel Toolbar. When Content Source Maps are detected on the page, a pencil icon will appear in the toolbar. Clicking this icon will enable Edit Mode, highlighting all editable fields on the page in blue.
|
||||
|
||||

|
||||
|
||||
### Troubleshooting
|
||||
## Troubleshooting
|
||||
|
||||
##### Dates
|
||||
### Date Fields
|
||||
|
||||
The plugin does not encode `date` fields by default, but for some cases like text that uses negative CSS letter-spacing, it may be necessary to split the encoded data out from the rendered text. This way you can safely use the cleaned data as expected.
|
||||
|
||||
@@ -91,7 +91,7 @@ import { vercelStegaSplit } from '@vercel/stega'
|
||||
const { cleaned, encoded } = vercelStegaSplit(text)
|
||||
```
|
||||
|
||||
##### Blocks
|
||||
### Block fields
|
||||
|
||||
All `blocks` fields by definition do not have plain text strings to encode. For this reason, blocks are given an additional `encodedSourceMap` key, which you can use to enable Content Link on entire sections of your site. You can then specify the editing container by adding the `data-vercel-edit-target` HTML attribute to any top-level element of your block.
|
||||
|
||||
|
||||
378
docs/lexical/converters.mdx
Normal file
378
docs/lexical/converters.mdx
Normal file
@@ -0,0 +1,378 @@
|
||||
---
|
||||
title: Lexical Converters
|
||||
label: Converters
|
||||
order: 20
|
||||
desc: Conversion between lexical, markdown and html
|
||||
keywords: lexical, rich text, editor, headless cms, convert, html, mdx, markdown, md, conversion, export
|
||||
---
|
||||
|
||||
|
||||
## Lexical => HTML
|
||||
|
||||
Lexical saves data in JSON, but can also generate its HTML representation via two main methods:
|
||||
|
||||
1. **Outputting HTML from the Collection:** Create a new field in your collection to convert saved JSON content to HTML. Payload generates and outputs the HTML for use in your frontend.
|
||||
2. **Generating HTML on any server** Convert JSON to HTML on-demand on the server.
|
||||
|
||||
The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
|
||||
|
||||
### Outputting HTML from the Collection
|
||||
|
||||
To add HTML generation directly within the collection, follow the example below:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import { HTMLConverterFeature, lexicalEditor, lexicalHTML } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: 'nameOfYourRichTextField',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
// The HTMLConverter Feature is the feature which manages the HTML serializers.
|
||||
// If you do not pass any arguments to it, it will use the default serializers.
|
||||
HTMLConverterFeature({}),
|
||||
],
|
||||
}),
|
||||
},
|
||||
lexicalHTML('nameOfYourRichTextField', { name: 'nameOfYourRichTextField_html' }),
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
The `lexicalHTML()` function creates a new field that automatically converts the referenced lexical richText field into HTML through an afterRead hook.
|
||||
|
||||
### Generating HTML anywhere on the server:
|
||||
|
||||
If you wish to convert JSON to HTML ad-hoc, use this code snippet:
|
||||
|
||||
```ts
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
import {
|
||||
type SanitizedEditorConfig,
|
||||
convertLexicalToHTML,
|
||||
consolidateHTMLConverters,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
|
||||
async function lexicalToHTML(
|
||||
editorData: SerializedEditorState,
|
||||
editorConfig: SanitizedEditorConfig,
|
||||
) {
|
||||
return await convertLexicalToHTML({
|
||||
converters: consolidateHTMLConverters({ editorConfig }),
|
||||
data: editorData,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This method employs `convertLexicalToHTML` from `@payloadcms/richtext-lexical`, which converts the serialized editor state into HTML.
|
||||
|
||||
Because every `Feature` is able to provide html converters, and because the `htmlFeature` can modify those or provide their own, we need to consolidate them with the default html Converters using the `consolidateHTMLConverters` function.
|
||||
|
||||
### CSS
|
||||
|
||||
Payload's lexical HTML converter does not generate CSS for you, but it does add classes to the generated HTML. You can use these classes to style the HTML in your frontend.
|
||||
|
||||
Here is some "base" CSS you can use to ensure that nested lists render correctly:
|
||||
|
||||
```css
|
||||
/* Base CSS for Lexical HTML */
|
||||
.nestedListItem, .list-check {
|
||||
list-style-type: none;
|
||||
}
|
||||
```
|
||||
|
||||
### Creating your own HTML Converter
|
||||
|
||||
HTML Converters are typed as `HTMLConverter`, which contains the node type it should handle, and a function that accepts the serialized node from the lexical editor, and outputs the HTML string. Here's the HTML Converter of the Upload node as an example:
|
||||
|
||||
```ts
|
||||
import type { HTMLConverter } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const UploadHTMLConverter: HTMLConverter<SerializedUploadNode> = {
|
||||
converter: async ({ node, payload }) => {
|
||||
const uploadDocument = await payload.findByID({
|
||||
id: node.value.id,
|
||||
collection: node.relationTo,
|
||||
})
|
||||
const url = (payload?.config?.serverURL || '') + uploadDocument?.url
|
||||
|
||||
if (!(uploadDocument?.mimeType as string)?.startsWith('image')) {
|
||||
// Only images can be serialized as HTML
|
||||
return ``
|
||||
}
|
||||
|
||||
return `<img src="${url}" alt="${uploadDocument?.filename}" width="${uploadDocument?.width}" height="${uploadDocument?.height}"/>`
|
||||
},
|
||||
nodeTypes: [UploadNode.getType()], // This is the type of the lexical node that this converter can handle. Instead of hardcoding 'upload' we can get the node type directly from the UploadNode, since it's static.
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, we have access to all the information saved in the node (for the Upload node, this is `value`and `relationTo`) and we can use that to generate the HTML.
|
||||
|
||||
The `convertLexicalToHTML` is part of `@payloadcms/richtext-lexical` automatically handles traversing the editor state and calling the correct converter for each node.
|
||||
|
||||
### Embedding the HTML Converter in your Feature
|
||||
|
||||
You can embed your HTML Converter directly within your custom `ServerFeature`, allowing it to be handled automatically by the `consolidateHTMLConverters` function. Here is an example:
|
||||
|
||||
```ts
|
||||
import { createNode } from '@payloadcms/richtext-lexical'
|
||||
import type { FeatureProviderProviderServer } from '@payloadcms/richtext-lexical'
|
||||
|
||||
export const UploadFeature: FeatureProviderProviderServer<
|
||||
UploadFeatureProps,
|
||||
UploadFeaturePropsClient
|
||||
> = (props) => {
|
||||
/*...*/
|
||||
return {
|
||||
feature: () => {
|
||||
return {
|
||||
nodes: [
|
||||
createNode({
|
||||
converters: {
|
||||
html: yourHTMLConverter, // <= This is where you define your HTML Converter
|
||||
},
|
||||
node: UploadNode,
|
||||
//...
|
||||
}),
|
||||
],
|
||||
ClientComponent: UploadFeatureClientComponent,
|
||||
clientFeatureProps: clientProps,
|
||||
serverFeatureProps: props,
|
||||
/*...*/
|
||||
}
|
||||
},
|
||||
key: 'upload',
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Headless Editor
|
||||
|
||||
Lexical provides a seamless way to perform conversions between various other formats:
|
||||
|
||||
- HTML to Lexical (or, importing HTML into the lexical editor)
|
||||
- Markdown to Lexical (or, importing Markdown into the lexical editor)
|
||||
- Lexical to Markdown
|
||||
|
||||
A headless editor can perform such conversions outside of the main editor instance. Follow this method to initiate a headless editor:
|
||||
|
||||
```ts
|
||||
import { createHeadlessEditor } from '@lexical/headless' // <= make sure this package is installed
|
||||
import { getEnabledNodes, sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const yourEditorConfig // <= your editor config here
|
||||
|
||||
const headlessEditor = createHeadlessEditor({
|
||||
nodes: getEnabledNodes({
|
||||
editorConfig: sanitizeServerEditorConfig(yourEditorConfig),
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
### Getting the editor config
|
||||
|
||||
As you can see, you need to provide an editor config in order to create a headless editor. This is because the editor config is used to determine which nodes & features are enabled, and which converters are used.
|
||||
|
||||
To get the editor config, simply import the default editor config and adjust it - just like you did inside of the `editor: lexicalEditor({})` property:
|
||||
|
||||
```ts
|
||||
import { defaultEditorConfig, defaultEditorFeatures } from '@payloadcms/richtext-lexical' // <= make sure this package is installed
|
||||
|
||||
const yourEditorConfig = defaultEditorConfig
|
||||
|
||||
// If you made changes to the features of the field's editor config, you should also make those changes here:
|
||||
yourEditorConfig.features = [
|
||||
...defaultEditorFeatures,
|
||||
// Add your custom features here
|
||||
]
|
||||
```
|
||||
|
||||
### Getting the editor config from an existing field
|
||||
|
||||
If you have access to the sanitized collection config, you can get access to the lexical sanitized editor config & features, as every lexical richText field returns it. Here is an example how you can get it from another field's afterRead hook:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig, RichTextField } from 'payload/types'
|
||||
import { createHeadlessEditor } from '@lexical/headless'
|
||||
import type { LexicalRichTextAdapter, SanitizedServerEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
import {
|
||||
getEnabledNodes,
|
||||
lexicalEditor
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
|
||||
export const MyCollection: CollectionConfig = {
|
||||
slug: 'slug',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
hooks: {
|
||||
afterRead: [
|
||||
({ value, collection }) => {
|
||||
const otherRichTextField: RichTextField = collection.fields.find(
|
||||
(field) => 'name' in field && field.name === 'richText',
|
||||
) as RichTextField
|
||||
|
||||
const lexicalAdapter: LexicalRichTextAdapter =
|
||||
otherRichTextField.editor as LexicalRichTextAdapter
|
||||
|
||||
const sanitizedServerEditorConfig: SanitizedServerEditorConfig =
|
||||
lexicalAdapter.editorConfig
|
||||
|
||||
const headlessEditor = createHeadlessEditor({
|
||||
nodes: getEnabledNodes({
|
||||
editorConfig: sanitizedServerEditorConfig,
|
||||
}),
|
||||
})
|
||||
|
||||
// Do whatever you want with the headless editor
|
||||
|
||||
return value
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features,
|
||||
}),
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## HTML => Lexical
|
||||
|
||||
Once you have your headless editor instance, you can use it to convert HTML to Lexical:
|
||||
|
||||
```ts
|
||||
import { $generateNodesFromDOM } from '@lexical/html'
|
||||
import { $getRoot, $getSelection } from 'lexical'
|
||||
import { JSDOM } from 'jsdom'
|
||||
|
||||
headlessEditor.update(
|
||||
() => {
|
||||
// In a headless environment you can use a package such as JSDom to parse the HTML string.
|
||||
const dom = new JSDOM(htmlString)
|
||||
|
||||
// Once you have the DOM instance it's easy to generate LexicalNodes.
|
||||
const nodes = $generateNodesFromDOM(headlessEditor, dom.window.document)
|
||||
|
||||
// Select the root
|
||||
$getRoot().select()
|
||||
|
||||
// Insert them at a selection.
|
||||
const selection = $getSelection()
|
||||
selection.insertNodes(nodes)
|
||||
},
|
||||
{ discrete: true },
|
||||
)
|
||||
|
||||
// Do this if you then want to get the editor JSON
|
||||
const editorJSON = headlessEditor.getEditorState().toJSON()
|
||||
```
|
||||
|
||||
Functions prefixed with a `$` can only be run inside an `editor.update()` or `editorState.read()` callback.
|
||||
|
||||
This has been taken from the [lexical serialization & deserialization docs](https://lexical.dev/docs/concepts/serialization#html---lexical).
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
Using the <code>discrete: true</code> flag ensures instant updates to the editor state. If
|
||||
immediate reading of the updated state isn't necessary, you can omit the flag.
|
||||
</Banner>
|
||||
|
||||
## Markdown => Lexical
|
||||
|
||||
Convert markdown content to the Lexical editor format with the following:
|
||||
|
||||
```ts
|
||||
import { $convertFromMarkdownString } from '@lexical/markdown'
|
||||
import { sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig) // <= your editor config here
|
||||
const markdown = `# Hello World`
|
||||
|
||||
headlessEditor.update(
|
||||
() => {
|
||||
$convertFromMarkdownString(markdown, yourSanitizedEditorConfig.features.markdownTransformers)
|
||||
},
|
||||
{ discrete: true },
|
||||
)
|
||||
|
||||
// Do this if you then want to get the editor JSON
|
||||
const editorJSON = headlessEditor.getEditorState().toJSON()
|
||||
```
|
||||
|
||||
## Lexical => Markdown
|
||||
|
||||
Export content from the Lexical editor into Markdown format using these steps:
|
||||
|
||||
1. Import your current editor state into the headless editor.
|
||||
2. Convert and fetch the resulting markdown string.
|
||||
|
||||
Here's the code for it:
|
||||
|
||||
```ts
|
||||
import { $convertToMarkdownString } from '@lexical/markdown'
|
||||
import { sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
|
||||
const yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig) // <= your editor config here
|
||||
const yourEditorState: SerializedEditorState // <= your current editor state here
|
||||
|
||||
// Import editor state into your headless editor
|
||||
try {
|
||||
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
|
||||
} catch (e) {
|
||||
logger.error({ err: e }, 'ERROR parsing editor state')
|
||||
}
|
||||
|
||||
// Export to markdown
|
||||
let markdown: string
|
||||
headlessEditor.getEditorState().read(() => {
|
||||
markdown = $convertToMarkdownString(yourSanitizedEditorConfig?.features?.markdownTransformers)
|
||||
})
|
||||
```
|
||||
|
||||
The `.setEditorState()` function immediately updates your editor state. Thus, there's no need for the `discrete: true` flag when reading the state afterward.
|
||||
|
||||
## Lexical => Plain Text
|
||||
|
||||
Export content from the Lexical editor into plain text using these steps:
|
||||
|
||||
1. Import your current editor state into the headless editor.
|
||||
2. Convert and fetch the resulting plain text string.
|
||||
|
||||
Here's the code for it:
|
||||
|
||||
```ts
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
import { $getRoot } from 'lexical'
|
||||
|
||||
const yourEditorState: SerializedEditorState // <= your current editor state here
|
||||
|
||||
// Import editor state into your headless editor
|
||||
try {
|
||||
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
|
||||
} catch (e) {
|
||||
logger.error({ err: e }, 'ERROR parsing editor state')
|
||||
}
|
||||
|
||||
// Export to plain text
|
||||
const plainTextContent =
|
||||
headlessEditor.getEditorState().read(() => {
|
||||
return $getRoot().getTextContent()
|
||||
}) || ''
|
||||
```
|
||||
182
docs/lexical/migration.mdx
Normal file
182
docs/lexical/migration.mdx
Normal file
@@ -0,0 +1,182 @@
|
||||
---
|
||||
title: Lexical Migration
|
||||
label: Migration
|
||||
order: 30
|
||||
desc: Migration from slate and payload-plugin-lexical to lexical
|
||||
keywords: lexical, rich text, editor, headless cms, migrate, migration
|
||||
---
|
||||
|
||||
## Migrating from Slate
|
||||
|
||||
While both Slate and Lexical save the editor state in JSON, the structure of the JSON is different.
|
||||
|
||||
### Migration via SlateToLexicalFeature
|
||||
|
||||
One way to handle this is to just give your lexical editor the ability to read the slate JSON.
|
||||
|
||||
Simply add the `SlateToLexicalFeature` to your editor:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import { SlateToLexicalFeature, lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: 'nameOfYourRichTextField',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [...defaultFeatures, SlateToLexicalFeature({})],
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
and done! Now, everytime this lexical editor is initialized, it converts the slate date to lexical on-the-fly. If the data is already in lexical format, it will just pass it through.
|
||||
|
||||
This is by far the easiest way to migrate from Slate to Lexical, although it does come with a few caveats:
|
||||
|
||||
- There is a performance hit when initializing the lexical editor
|
||||
- The editor will still output the Slate data in the output JSON, as the on-the-fly converter only runs for the admin panel
|
||||
|
||||
The easy way to solve this: Just save the document! This overrides the slate data with the lexical data, and the next time the document is loaded, the lexical data will be used. This solves both the performance and the output issue for that specific document.
|
||||
|
||||
### Migration via migration script
|
||||
|
||||
The method described above does not solve the issue for all documents, though. If you want to convert all your documents to lexical, you can use a migration script. Here's a simple example:
|
||||
|
||||
```ts
|
||||
import type { Payload } from 'payload'
|
||||
import type { YourDocumentType } from 'payload/generated-types'
|
||||
|
||||
import {
|
||||
cloneDeep,
|
||||
convertSlateToLexical,
|
||||
defaultSlateConverters,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
|
||||
import { AnotherCustomConverter } from './lexicalFeatures/converters/AnotherCustomConverter'
|
||||
|
||||
export async function convertAll(payload: Payload, collectionName: string, fieldName: string) {
|
||||
const docs: YourDocumentType[] = await payload.db.collections[collectionName].find({}).exec() // Use MongoDB models directly to query all documents at once
|
||||
console.log(`Found ${docs.length} ${collectionName} docs`)
|
||||
|
||||
const converters = cloneDeep([...defaultSlateConverters, AnotherCustomConverter])
|
||||
|
||||
// Split docs into batches of 20.
|
||||
const batchSize = 20
|
||||
const batches = []
|
||||
for (let i = 0; i < docs.length; i += batchSize) {
|
||||
batches.push(docs.slice(i, i + batchSize))
|
||||
}
|
||||
|
||||
let processed = 0 // Number of processed docs
|
||||
|
||||
for (const batch of batches) {
|
||||
// Process each batch asynchronously
|
||||
const promises = batch.map(async (doc: YourDocumentType) => {
|
||||
const richText = doc[fieldName]
|
||||
|
||||
if (richText && Array.isArray(richText) && !('root' in richText)) {
|
||||
// It's Slate data - skip already-converted data
|
||||
const converted = convertSlateToLexical({
|
||||
converters: converters,
|
||||
slateData: richText,
|
||||
})
|
||||
|
||||
await payload.update({
|
||||
id: doc.id,
|
||||
collection: collectionName as any,
|
||||
depth: 0, // performance optimization. No need to run population.
|
||||
data: {
|
||||
[fieldName]: converted,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Wait for all promises in the batch to complete. Resolving batches of 20 asynchronously is faster than waiting for each doc to update individually
|
||||
await Promise.all(promises)
|
||||
|
||||
// Update the count of processed docs
|
||||
processed += batch.length
|
||||
console.log(`Converted ${processed} of ${docs.length}`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `convertSlateToLexical` is the same method used in the `SlateToLexicalFeature` - it handles traversing the Slate JSON for you.
|
||||
|
||||
Do note that this script might require adjustment depending on your document structure, especially if you have nested richText fields or localization enabled.
|
||||
|
||||
### Converting custom Slate nodes
|
||||
|
||||
If you have custom Slate nodes, create a custom converter for them. Here's the Upload converter as an example:
|
||||
|
||||
```ts
|
||||
import type { SerializedUploadNode } from '../uploadNode.'
|
||||
import type { SlateNodeConverter } from '@payloadcms/richtext-lexical'
|
||||
|
||||
export const SlateUploadConverter: SlateNodeConverter = {
|
||||
converter({ slateNode }) {
|
||||
return {
|
||||
fields: {
|
||||
...slateNode.fields,
|
||||
},
|
||||
format: '',
|
||||
relationTo: slateNode.relationTo,
|
||||
type: 'upload',
|
||||
value: {
|
||||
id: slateNode.value?.id || '',
|
||||
},
|
||||
version: 1,
|
||||
} as const as SerializedUploadNode
|
||||
},
|
||||
nodeTypes: ['upload'],
|
||||
}
|
||||
```
|
||||
|
||||
It's pretty simple: You get a Slate node as input, and you return the lexical node. The `nodeTypes` array is used to determine which Slate nodes this converter can handle.
|
||||
|
||||
When using a migration script, you can add your custom converters to the `converters` property of the `convertSlateToLexical` props, as seen in the example above
|
||||
|
||||
When using the `SlateToLexicalFeature`, you can add your custom converters to the `converters` property of the `SlateToLexicalFeature` props:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import {
|
||||
SlateToLexicalFeature,
|
||||
lexicalEditor,
|
||||
defaultSlateConverters,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
|
||||
import { YourCustomConverter } from '../converters/YourCustomConverter'
|
||||
|
||||
const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: 'nameOfYourRichTextField',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
SlateToLexicalFeature({
|
||||
converters: [...defaultSlateConverters, YourCustomConverter],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Migrating from payload-plugin-lexical
|
||||
|
||||
Migrating from [payload-plugin-lexical](https://github.com/AlessioGr/payload-plugin-lexical) works similar to migrating from Slate.
|
||||
|
||||
Instead of a `SlateToLexicalFeature` there is a `LexicalPluginToLexicalFeature` you can use. And instead of `convertSlateToLexical` you can use `convertLexicalPluginToLexical`.
|
||||
179
docs/lexical/overview.mdx
Normal file
179
docs/lexical/overview.mdx
Normal file
@@ -0,0 +1,179 @@
|
||||
---
|
||||
title: Lexical Overview
|
||||
label: Overview
|
||||
order: 10
|
||||
desc: Built by Meta, Lexical is an incredibly powerful rich text editor, and it works beautifully within Payload.
|
||||
keywords: lexical, rich text, editor, headless cms
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
<Banner type="warning">
|
||||
Payload's Lexical rich text editor is currently in beta. It's stable enough to use as you build on
|
||||
Payload, so if you're up for helping us fine-tune it, you should use it. But if you're looking for
|
||||
stability, use Slate instead.
|
||||
</Banner>
|
||||
|
||||
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
|
||||
1. A "hover" toolbar that pops up if you select text
|
||||
1. It supports Payload blocks natively, directly within your rich text editor
|
||||
1. Custom elements, called "features", are much easier to build in Lexical vs. Slate
|
||||
|
||||
To use the Lexical editor, first you need to install it:
|
||||
|
||||
```
|
||||
npm install @payloadcms/richtext-lexical
|
||||
```
|
||||
|
||||
Once you have it installed, you can pass it to your top-level Payload config as follows:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
|
||||
export default buildConfig({
|
||||
collections: [
|
||||
// your collections here
|
||||
],
|
||||
// Pass the Lexical editor to the root config
|
||||
editor: lexicalEditor({}),
|
||||
})
|
||||
```
|
||||
|
||||
You can also override Lexical settings on a field-by-field basis as follows:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
// Pass the Lexical editor here and override base settings as necessary
|
||||
editor: lexicalEditor({}),
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Extending the lexical editor with Features
|
||||
|
||||
Lexical has been designed with extensibility in mind. Whether you're aiming to introduce new functionalities or tweak the existing ones, Lexical makes it seamless for you to bring those changes to life.
|
||||
|
||||
### Features: The Building Blocks
|
||||
|
||||
At the heart of Lexical's customization potential are "features". While Lexical ships with a set of default features we believe are essential for most use cases, the true power lies in your ability to redefine, expand, or prune these as needed.
|
||||
|
||||
If you remove all the default features, you're left with a blank editor. You can then add in only the features you need, or you can build your own custom features from scratch.
|
||||
|
||||
### Integrating New Features
|
||||
|
||||
To weave in your custom features, utilize the `features` prop when initializing the Lexical Editor. Here's a basic example of how this is done:
|
||||
|
||||
```ts
|
||||
import {
|
||||
BlocksFeature,
|
||||
LinkFeature,
|
||||
UploadFeature,
|
||||
lexicalEditor,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
import { Banner } from '../blocks/Banner'
|
||||
import { CallToAction } from '../blocks/CallToAction'
|
||||
|
||||
{
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
LinkFeature({
|
||||
// Example showing how to customize the built-in fields
|
||||
// of the Link feature
|
||||
fields: ({ defaultFields }) => [
|
||||
...defaultFields,
|
||||
{
|
||||
name: 'rel',
|
||||
label: 'Rel Attribute',
|
||||
type: 'select',
|
||||
hasMany: true,
|
||||
options: ['noopener', 'noreferrer', 'nofollow'],
|
||||
admin: {
|
||||
description:
|
||||
'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
UploadFeature({
|
||||
collections: {
|
||||
uploads: {
|
||||
// Example showing how to customize the built-in fields
|
||||
// of the Upload feature
|
||||
fields: [
|
||||
{
|
||||
name: 'caption',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor(),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
// This is incredibly powerful. You can re-use your Payload blocks
|
||||
// directly in the Lexical editor as follows:
|
||||
BlocksFeature({
|
||||
blocks: [Banner, CallToAction],
|
||||
}),
|
||||
],
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Features overview
|
||||
|
||||
Here's an overview of all the included features:
|
||||
|
||||
| Feature Name | Included by default | Description |
|
||||
|--------------------------------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`BoldTextFeature`** | Yes | Handles the bold text format |
|
||||
| **`ItalicTextFeature`** | Yes | Handles the italic text format |
|
||||
| **`UnderlineTextFeature`** | Yes | Handles the underline text format |
|
||||
| **`StrikethroughTextFeature`** | Yes | Handles the strikethrough text format |
|
||||
| **`SubscriptTextFeature`** | Yes | Handles the subscript text format |
|
||||
| **`SuperscriptTextFeature`** | Yes | Handles the superscript text format |
|
||||
| **`InlineCodeTextFeature`** | Yes | Handles the inline-code text format |
|
||||
| **`ParagraphFeature`** | Yes | Handles paragraphs. Since they are already a key feature of lexical itself, this Feature mainly handles the Slash and Add-Block menu entries for paragraphs |
|
||||
| **`HeadingFeature`** | Yes | Adds Heading Nodes (by default, H1 - H6, but that can be customized) |
|
||||
| **`AlignFeature`** | Yes | Allows you to align text left, centered and right |
|
||||
| **`IndentFeature`** | Yes | Allows you to indent text with the tab key |
|
||||
| **`UnorderedListFeature`** | Yes | Adds unordered lists (ul) |
|
||||
| **`OrderedListFeature`** | Yes | Adds ordered lists (ol) |
|
||||
| **`CheckListFeature`** | Yes | Adds checklists |
|
||||
| **`LinkFeature`** | Yes | Allows you to create internal and external links |
|
||||
| **`RelationshipFeature`** | Yes | Allows you to create block-level (not inline) relationships to other documents |
|
||||
| **`BlockQuoteFeature`** | Yes | Allows you to create block-level quotes |
|
||||
| **`UploadFeature`** | Yes | Allows you to create block-level upload nodes - this supports all kinds of uploads, not just images |
|
||||
| **`HorizontalRuleFeature`** | Yes | Horizontal rules / separators. Basically displays an `<hr>` element |
|
||||
| **`InlineToolbarFeature`** | Yes | The inline toolbar is the floating toolbar which appears when you select text. This toolbar only contains actions relevant for selected text |
|
||||
| **`FixedToolbarFeature`** | No | This classic toolbar is pinned to the top and always visible. Both inline and fixed toolbars can be enabled at the same time. |
|
||||
| **`BlocksFeature`** | No | Allows you to use Payload's [Blocks Field](/docs/fields/blocks) directly inside your editor. In the feature props, you can specify the allowed blocks - just like in the Blocks field. |
|
||||
| **`TreeViewFeature`** | No | Adds a debug box under the editor, which allows you to see the current editor state live, the dom, as well as time travel. Very useful for debugging |
|
||||
|
||||
Notice how even the toolbars are features? That's how extensible our lexical editor is - you could theoretically create your own toolbar if you wanted to!
|
||||
|
||||
## Creating your own, custom Feature
|
||||
|
||||
Creating your own custom feature requires deep knowledge of the Lexical editor. We recommend you take a look at the [Lexical documentation](https://lexical.dev/docs/intro) first - especially the "concepts" section.
|
||||
|
||||
Next, take a look at the [features we've already built](https://github.com/payloadcms/payload/tree/main/packages/richtext-lexical/src/field/features) - understanding how they work will help you understand how to create your own. There is no difference between the features included by default and the ones you create yourself - since those features are all isolated from the "core", you have access to the same APIs, whether the feature is part of payload or not!
|
||||
|
||||
## Coming Soon
|
||||
|
||||
Lots more documentation will be coming soon, which will show in detail how to create your own custom features within Lexical.
|
||||
|
||||
For now, take a look at the TypeScript interfaces and let us know if you need a hand. Much more will be coming from the Payload team on this topic soon.
|
||||
@@ -44,7 +44,7 @@ And return the following values:
|
||||
It is important that the `depth` argument matches exactly with the depth of your initial page request. The depth property is used to populated relationships and uploads beyond their IDs. See [Depth](../getting-started/concepts#depth) for more information.
|
||||
</Banner>
|
||||
|
||||
### React
|
||||
## React
|
||||
|
||||
If your front-end application is built with client-side [React](https://react.dev) like [Next.js Pages Router](https://nextjs.org/docs/pages), you can use the `useLivePreview` hook that Payload provides.
|
||||
|
||||
@@ -79,7 +79,7 @@ export const PageClient: React.FC<{
|
||||
}
|
||||
```
|
||||
|
||||
### Vue
|
||||
## Vue
|
||||
|
||||
If your front-end application is built with [Vue 3](https://vuejs.org) or [Nuxt 3](https://nuxt.js), you can use the `useLivePreview` composable that Payload provides.
|
||||
|
||||
@@ -114,7 +114,7 @@ const { data } = useLivePreview<PageData>({
|
||||
</template>
|
||||
```
|
||||
|
||||
### Building your own hook
|
||||
## Building your own hook
|
||||
|
||||
No matter what front-end framework you are using, you can build your own hook using the same underlying tooling that Payload provides.
|
||||
|
||||
@@ -232,7 +232,7 @@ For a working demonstration of this, check out the official [Live Preview Exampl
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
#### Relationships and/or uploads are not populating
|
||||
### Relationships and/or uploads are not populating
|
||||
|
||||
If you are using relationships or uploads in your front-end application, and your front-end application runs on a different domain than your Payload server, you may need to configure [CORS](../configuration/overview) to allow requests to be made between the two domains. This includes sites that are running on a different port or subdomain. Similarly, if you are protecting resources behind user authentication, you may also need to configure [CSRF](../authentication/overview#csrf-protection) to allow cookies to be sent between the two domains. For example:
|
||||
|
||||
@@ -257,7 +257,7 @@ If you are using relationships or uploads in your front-end application, and you
|
||||
}
|
||||
```
|
||||
|
||||
#### Relationships and/or uploads disappear after editing a document
|
||||
### Relationships and/or uploads disappear after editing a document
|
||||
|
||||
It is possible that either you are setting an improper [`depth`](../getting-started/concepts#depth) in your initial request and/or your `useLivePreview` hook, or they're mismatched. Ensure that the `depth` parameter is set to the correct value, and that it matches exactly in both places. For example:
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Server-side Live Preview works by making a roundtrip to the server every time yo
|
||||
|
||||
If your front-end application is built with [React](#react), you can use the `RefreshRouteOnChange` function that Payload provides. In the future, all other major frameworks like Vue and Svelte will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own router refresh component](#building-your-own-router-refresh-component) for more information.
|
||||
|
||||
### React
|
||||
## React
|
||||
|
||||
If your front-end application is built with [React](https://react.dev) or [Next.js](https://nextjs.org), you can use the `RefreshRouteOnSave` component that Payload provides.
|
||||
|
||||
@@ -157,7 +157,7 @@ export const RefreshRouteOnSave: React.FC<{
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
#### Updates do not appear as fast as client-side Live Preview
|
||||
### Updates do not appear as fast as client-side Live Preview
|
||||
|
||||
If you are noticing that updates feel less snappy than client-side Live Preview (i.e. the `useLivePreview` hook), this is because of how the two differ in how they work—instead of emitting events against form state as you type, server-side Live Preview refreshes the route after a new document is saved. You can use autosave to mimic this effect. Try decreasing the value of `versions.autoSave.interval` to make the experience feel more responsive:
|
||||
|
||||
|
||||
@@ -26,11 +26,11 @@ Here are some common examples of how you can use the Local API:
|
||||
- Opening custom Express routes which feature additional functionality but still rely on Payload
|
||||
- Within access control and hook functions
|
||||
|
||||
### Accessing payload
|
||||
## Accessing payload
|
||||
|
||||
You can gain access to the currently running `payload` object via two ways:
|
||||
|
||||
##### Importing it
|
||||
#### Importing it
|
||||
|
||||
You can import or require `payload` into your own files after it's been initialized, but you need to make sure that
|
||||
your `import` / `require` statements come **after** you call `payload.init()`—otherwise Payload won't have been
|
||||
@@ -49,7 +49,7 @@ const afterChangeHook: CollectionAfterChangeHook = async () => {
|
||||
}
|
||||
```
|
||||
|
||||
##### Accessing from the `req`
|
||||
#### Accessing from the `req`
|
||||
|
||||
Payload is available anywhere you have access to the Express `req` - including within your access control and hook
|
||||
functions.
|
||||
@@ -64,7 +64,7 @@ const afterChangeHook: CollectionAfterChangeHook = async ({ req: { payload } })
|
||||
}
|
||||
```
|
||||
|
||||
### Local options available
|
||||
## Local options available
|
||||
|
||||
You can specify more options within the Local API vs. REST or GraphQL due to the server-only context that they are
|
||||
executed in.
|
||||
@@ -95,7 +95,7 @@ _There are more options available on an operation by operation basis outlined be
|
||||
|
||||
The following Collection operations are available through the Local API:
|
||||
|
||||
#### Create
|
||||
### Create
|
||||
|
||||
```js
|
||||
// The created Post document is returned
|
||||
@@ -127,7 +127,7 @@ const post = await payload.create({
|
||||
})
|
||||
```
|
||||
|
||||
#### Find
|
||||
### Find
|
||||
|
||||
```js
|
||||
// Result will be a paginated set of Posts.
|
||||
@@ -148,7 +148,7 @@ const result = await payload.find({
|
||||
})
|
||||
```
|
||||
|
||||
#### Find by ID
|
||||
### Find by ID
|
||||
|
||||
```js
|
||||
// Result will be a Post document.
|
||||
@@ -164,7 +164,7 @@ const result = await payload.findByID({
|
||||
})
|
||||
```
|
||||
|
||||
#### Count
|
||||
### Count
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
@@ -180,7 +180,7 @@ const result = await payload.count({
|
||||
})
|
||||
```
|
||||
|
||||
#### Update by ID
|
||||
### Update by ID
|
||||
|
||||
```js
|
||||
// Result will be the updated Post document.
|
||||
@@ -211,7 +211,7 @@ const result = await payload.update({
|
||||
})
|
||||
```
|
||||
|
||||
#### Update Many
|
||||
### Update Many
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
@@ -249,7 +249,7 @@ const result = await payload.update({
|
||||
})
|
||||
```
|
||||
|
||||
#### Delete
|
||||
### Delete
|
||||
|
||||
```js
|
||||
// Result will be the now-deleted Post document.
|
||||
@@ -265,7 +265,7 @@ const result = await payload.delete({
|
||||
})
|
||||
```
|
||||
|
||||
#### Delete Many
|
||||
### Delete Many
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
@@ -293,7 +293,7 @@ const result = await payload.delete({
|
||||
If a collection has [`Authentication`](/docs/authentication/overview) enabled, additional Local API operations will be
|
||||
available:
|
||||
|
||||
#### Login
|
||||
### Login
|
||||
|
||||
```js
|
||||
// result will be formatted as follows:
|
||||
@@ -320,7 +320,7 @@ const result = await payload.login({
|
||||
})
|
||||
```
|
||||
|
||||
#### Forgot Password
|
||||
### Forgot Password
|
||||
|
||||
```js
|
||||
// Returned token will allow for a password reset
|
||||
@@ -334,7 +334,7 @@ const token = await payload.forgotPassword({
|
||||
})
|
||||
```
|
||||
|
||||
#### Reset Password
|
||||
### Reset Password
|
||||
|
||||
```js
|
||||
// Result will be formatted as follows:
|
||||
@@ -354,7 +354,7 @@ const result = await payload.resetPassword({
|
||||
})
|
||||
```
|
||||
|
||||
#### Unlock
|
||||
### Unlock
|
||||
|
||||
```js
|
||||
// Returned result will be a boolean representing success or failure
|
||||
@@ -369,7 +369,7 @@ const result = await payload.unlock({
|
||||
})
|
||||
```
|
||||
|
||||
#### Verify
|
||||
### Verify
|
||||
|
||||
```js
|
||||
// Returned result will be a boolean representing success or failure
|
||||
@@ -383,7 +383,7 @@ const result = await payload.verifyEmail({
|
||||
|
||||
The following Global operations are available through the Local API:
|
||||
|
||||
#### Find
|
||||
### Find
|
||||
|
||||
```js
|
||||
// Result will be the Header Global.
|
||||
@@ -398,7 +398,7 @@ const result = await payload.findGlobal({
|
||||
})
|
||||
```
|
||||
|
||||
#### Update
|
||||
### Update
|
||||
|
||||
```js
|
||||
// Result will be the updated Header Global.
|
||||
|
||||
@@ -22,11 +22,11 @@ Our plugin template includes everything you need to build a full life-cycle plug
|
||||
|
||||
By abstracting your code into a plugin, you'll be able to reuse your feature across multiple projects and make it available for other developers to use.
|
||||
|
||||
### Plugins Recap
|
||||
## Plugins Recap
|
||||
|
||||
Here is a brief recap of how to integrate plugins with Payload, to learn more head back to the [plugin overview page](https://payloadcms.com/docs/plugins/overview).
|
||||
|
||||
#### How to install a plugin
|
||||
### How to install a plugin
|
||||
|
||||
To install any plugin, simply add it to your Payload config in the plugins array.
|
||||
|
||||
@@ -45,7 +45,7 @@ const config = buildConfig({
|
||||
export default config;
|
||||
```
|
||||
|
||||
#### Initialization
|
||||
### Initialization
|
||||
|
||||
The initialization process goes in the following order:
|
||||
|
||||
@@ -55,7 +55,7 @@ The initialization process goes in the following order:
|
||||
4. Sanitization cleans and validates data
|
||||
5. Final config gets initialized
|
||||
|
||||
### Plugin Template
|
||||
## Plugin Template
|
||||
|
||||
In the [Payload plugin template](https://github.com/payloadcms/payload-plugin-template), you will see a common file structure that is used across plugins:
|
||||
|
||||
@@ -63,14 +63,14 @@ In the [Payload plugin template](https://github.com/payloadcms/payload-plugin-te
|
||||
2. /src folder - everything related to the plugin
|
||||
3. /dev folder - sanitized test project for development
|
||||
|
||||
#### Root
|
||||
### The root folder
|
||||
|
||||
In the root folder, you will see various files related to the configuration of the plugin. We set up our environment in a similar manner in Payload core and across other projects. The only two files you need to modify are:
|
||||
|
||||
- **README**.md - This contains instructions on how to use the template. When you are ready, update this to contain instructions on how to use your Plugin.
|
||||
- **package**.json - Contains necessary scripts and dependencies. Overwrite the metadata in this file to describe your Plugin.
|
||||
|
||||
#### Dev
|
||||
### The dev folder
|
||||
|
||||
The purpose of the **dev** folder is to provide a sanitized local Payload project. so you can run and test your plugin while you are actively developing it.
|
||||
|
||||
@@ -101,7 +101,7 @@ When you're ready to start development, navigate into this folder with `cd
|
||||
|
||||
And then start the project with `yarn dev` and pull up `http://localhost:3000` in your browser.
|
||||
|
||||
### Testing
|
||||
## Testing
|
||||
|
||||
Another benefit of the dev folder is that you have the perfect environment established for testing.
|
||||
|
||||
@@ -129,7 +129,7 @@ describe('Plugin tests', () => {
|
||||
})
|
||||
```
|
||||
|
||||
### Seeding data
|
||||
## Seeding data
|
||||
|
||||
For development and testing, you will likely need some data to work with. You can streamline this process by seeding and dropping your database - instead of manually entering data.
|
||||
|
||||
@@ -159,7 +159,7 @@ export const seed = async (payload: Payload): Promise<void> => {
|
||||
|
||||
```
|
||||
|
||||
#### Src
|
||||
## Overview of the src folder
|
||||
|
||||
Now that we have our environment setup and dev project ready to go - it's time to build the plugin!
|
||||
|
||||
@@ -188,7 +188,7 @@ export const samplePlugin =
|
||||
3. From here, you can extend the config however you like!
|
||||
4. Finally, return the config and you're all set.
|
||||
|
||||
### Spread Syntax
|
||||
## Spread Syntax
|
||||
|
||||
[Spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) (or the spread operator) is a feature in JavaScript that uses the dot notation **(...)** to spread elements from arrays, strings, or objects into various contexts.
|
||||
|
||||
@@ -235,7 +235,7 @@ If you wish to add to the `onInit`, you must include the async/await. We don&apo
|
||||
|
||||
In the template, we have stubbed out a basic `onInitExtension` file that you can use, if not needed feel free to delete it.
|
||||
|
||||
### Webpack
|
||||
## Webpack
|
||||
|
||||
If any of your files use server only packages such as fs, stripe, nodemailer, etc, they will need to be removed from the browser bundle. To do that, you can [alias the file imports with webpack](https://payloadcms.com/docs/admin/webpack#aliasing-server-only-modules).
|
||||
|
||||
@@ -251,7 +251,7 @@ config.admin = {
|
||||
}
|
||||
```
|
||||
|
||||
### Types
|
||||
## Types
|
||||
|
||||
If your plugin has options, you should define and provide types for these options in a separate file which gets exported from the main `index.ts`.
|
||||
|
||||
@@ -268,26 +268,26 @@ export interface PluginTypes {
|
||||
|
||||
If possible, include [JSDoc comments](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#types-1) to describe the options and their types. This allows a developer to see details about the options in their editor.
|
||||
|
||||
### Best practices
|
||||
## Best practices
|
||||
|
||||
In addition to the setup covered above, here are other best practices to follow:
|
||||
|
||||
##### Providing an enable / disable option:
|
||||
### Providing an enable / disable option
|
||||
|
||||
For a better user experience, provide a way to disable the plugin without uninstalling it. This is especially important if your plugin adds additional webpack aliases, this will allow you to still let the webpack run to prevent errors.
|
||||
|
||||
##### Include tests in your GitHub CI workflow:
|
||||
### Include tests in your GitHub CI workflow
|
||||
|
||||
If you've configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs)
|
||||
|
||||
##### Publish your finished plugin to NPM:
|
||||
### Publish your finished plugin to NPM
|
||||
|
||||
The best way to share and allow others to use your plugin once it is complete is to publish an NPM package. This process is straightforward and well documented, find out more about [creating and publishing a NPM package here](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
|
||||
|
||||
##### Add payload-plugin topic tag:
|
||||
### Add payload-plugin topic tag
|
||||
|
||||
Apply the tag **payload-plugin** to your GitHub repository. This will boost the visibility of your plugin and ensure it gets listed with [existing payload plugins](https://github.com/topics/payload-plugin).
|
||||
|
||||
##### Use [Semantic Versioning](https://semver.org/) (SemVer):
|
||||
### Use Semantic Versioning (SemVer)
|
||||
|
||||
With the SemVer system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.
|
||||
With the [Semantic Versioning](https://semver.org/) (SemVer) system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.
|
||||
|
||||
@@ -23,7 +23,7 @@ Forms can be as simple or complex as you need, from a basic contact form, to a m
|
||||
with as much detail as possible.
|
||||
</Banner>
|
||||
|
||||
##### Core Features
|
||||
## Core Features
|
||||
|
||||
- Build completely dynamic forms directly from the admin panel for a variety of use cases
|
||||
- Render forms on your front-end using your own UI components and match your brand's design system
|
||||
@@ -64,9 +64,9 @@ const config = buildConfig({
|
||||
export default config
|
||||
```
|
||||
|
||||
### Options
|
||||
## Options
|
||||
|
||||
#### `fields` (option)
|
||||
### `fields` (option)
|
||||
|
||||
The `fields` property is an object of field types to allow your admin editors to build forms with. To override default settings, pass either a boolean value or a partial [Payload Block](https://payloadcms.com/docs/fields/blocks#block-configs) _keyed to the block's slug_. See [Fields](#fields) for more details.
|
||||
|
||||
@@ -89,7 +89,7 @@ formBuilder({
|
||||
})
|
||||
```
|
||||
|
||||
#### `redirectRelationships`
|
||||
### `redirectRelationships`
|
||||
|
||||
The `redirectRelationships` property is an array of collection slugs that, when enabled, are populated as options in the form's `redirect` field. This field is used to redirect the user to a dedicated confirmation page upon form submission (optional).
|
||||
|
||||
@@ -101,7 +101,7 @@ formBuilder({
|
||||
})
|
||||
```
|
||||
|
||||
#### `beforeEmail`
|
||||
### `beforeEmail`
|
||||
|
||||
The `beforeEmail` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.
|
||||
|
||||
@@ -119,7 +119,7 @@ formBuilder({
|
||||
})
|
||||
```
|
||||
|
||||
#### `formOverrides`
|
||||
### `formOverrides`
|
||||
|
||||
Override anything on the `forms` collection by sending a [Payload Collection Config](https://payloadcms.com/docs/configuration/collections) to the `formOverrides` property.
|
||||
|
||||
@@ -143,7 +143,7 @@ formBuilder({
|
||||
})
|
||||
```
|
||||
|
||||
#### `formSubmissionOverrides`
|
||||
### `formSubmissionOverrides`
|
||||
|
||||
Override anything on the `form-submissions` collection by sending a [Payload Collection Config](https://payloadcms.com/docs/configuration/collections) to the `formSubmissionOverrides` property.
|
||||
|
||||
@@ -166,7 +166,7 @@ formBuilder({
|
||||
})
|
||||
```
|
||||
|
||||
#### `handlePayment`
|
||||
### `handlePayment`
|
||||
|
||||
The `handlePayment` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called upon form submission. You can integrate into any third-party payment processing API here to accept payment based on form input. You can use the `getPaymentTotal` function to calculate the total cost after all conditions have been applied. This is only applicable if the form has enabled the `payment` field.
|
||||
|
||||
@@ -206,7 +206,7 @@ Each field represents a form input. To override default settings pass either a b
|
||||
of a collection_ which are set via `formOverrides.fields`.
|
||||
</Banner>
|
||||
|
||||
#### Text
|
||||
### Text
|
||||
|
||||
Maps to a `text` input in your front-end. Used to collect a simple string.
|
||||
|
||||
@@ -218,7 +218,7 @@ Maps to a `text` input in your front-end. Used to collect a simple string.
|
||||
| `width` | string | The width of the field on the front-end. |
|
||||
| `required` | checkbox | Whether or not the field is required when submitted. |
|
||||
|
||||
#### Textarea
|
||||
### Textarea
|
||||
|
||||
Maps to a `textarea` input on your front-end. Used to collect a multi-line string.
|
||||
|
||||
@@ -230,7 +230,7 @@ Maps to a `textarea` input on your front-end. Used to collect a multi-line strin
|
||||
| `width` | string | The width of the field on the front-end. |
|
||||
| `required` | checkbox | Whether or not the field is required when submitted. |
|
||||
|
||||
#### Select
|
||||
### Select
|
||||
|
||||
Maps to a `select` input on your front-end. Used to display a list of options.
|
||||
|
||||
@@ -243,7 +243,7 @@ Maps to a `select` input on your front-end. Used to display a list of options.
|
||||
| `required` | checkbox | Whether or not the field is required when submitted. |
|
||||
| `options` | array | An array of objects with `label` and `value` properties. |
|
||||
|
||||
#### Email (field)
|
||||
### Email (field)
|
||||
|
||||
Maps to a `text` input with type `email` on your front-end. Used to collect an email address.
|
||||
|
||||
@@ -255,7 +255,7 @@ Maps to a `text` input with type `email` on your front-end. Used to collect an e
|
||||
| `width` | string | The width of the field on the front-end. |
|
||||
| `required` | checkbox | Whether or not the field is required when submitted. |
|
||||
|
||||
#### State
|
||||
### State
|
||||
|
||||
Maps to a `select` input on your front-end. Used to collect a US state.
|
||||
|
||||
@@ -267,7 +267,7 @@ Maps to a `select` input on your front-end. Used to collect a US state.
|
||||
| `width` | string | The width of the field on the front-end. |
|
||||
| `required` | checkbox | Whether or not the field is required when submitted. |
|
||||
|
||||
#### Country
|
||||
### Country
|
||||
|
||||
Maps to a `select` input on your front-end. Used to collect a country.
|
||||
|
||||
@@ -279,7 +279,7 @@ Maps to a `select` input on your front-end. Used to collect a country.
|
||||
| `width` | string | The width of the field on the front-end. |
|
||||
| `required` | checkbox | Whether or not the field is required when submitted. |
|
||||
|
||||
#### Checkbox
|
||||
### Checkbox
|
||||
|
||||
Maps to a `checkbox` input on your front-end. Used to collect a boolean value.
|
||||
|
||||
@@ -291,7 +291,7 @@ Maps to a `checkbox` input on your front-end. Used to collect a boolean value.
|
||||
| `width` | string | The width of the field on the front-end. |
|
||||
| `required` | checkbox | Whether or not the field is required when submitted. |
|
||||
|
||||
#### Number
|
||||
### Number
|
||||
|
||||
Maps to a `number` input on your front-end. Used to collect a number.
|
||||
|
||||
@@ -303,7 +303,7 @@ Maps to a `number` input on your front-end. Used to collect a number.
|
||||
| `width` | string | The width of the field on the front-end. |
|
||||
| `required` | checkbox | Whether or not the field is required when submitted. | | `defaultValue` | number | The default value of the field. |
|
||||
|
||||
#### Message
|
||||
### Message
|
||||
|
||||
Maps to a `RichText` component on your front-end. Used to display an arbitrary message to the user anywhere in the form.
|
||||
|
||||
@@ -311,7 +311,7 @@ Maps to a `RichText` component on your front-end. Used to display an arbitrary m
|
||||
| --------- | -------- | ----------------------------------- |
|
||||
| `message` | richText | The message to display on the form. |
|
||||
|
||||
#### Payment
|
||||
### Payment
|
||||
|
||||
Add this field to your form if it should collect payment. Upon submission, the `handlePayment` callback is executed with the form and submission data. You can use this to integrate with any third-party payment processing API.
|
||||
|
||||
@@ -324,7 +324,7 @@ Add this field to your form if it should collect payment. Upon submission, the `
|
||||
| `required` | checkbox | Whether or not the field is required when submitted. |
|
||||
| `priceConditions` | array | An array of objects that define the price conditions. See below for more details. |
|
||||
|
||||
##### Price Conditions
|
||||
#### Price Conditions
|
||||
|
||||
Each of the `priceConditions` are executed by the `getPaymentTotal` utility that this plugin provides. You can call this function in your `handlePayment` callback to dynamically calculate the total price of a form upon submission based on the user's input. For example, you could create a price condition that says "if the user selects 'yes' for this checkbox, add $10 to the total price".
|
||||
|
||||
@@ -337,7 +337,7 @@ Each of the `priceConditions` are executed by the `getPaymentTotal` utility that
|
||||
| `valueType` | string | The type of value to use to determine the price. |
|
||||
| `value` | string | The value to use to determine the price. |
|
||||
|
||||
#### Field Overrides
|
||||
### Field Overrides
|
||||
|
||||
You can provide your own custom fields by passing a new [Payload Block](https://payloadcms.com/docs/fields/blocks#block-configs) object into `fields`. You can override or extend any existing fields by first importing the `fields` from the plugin:
|
||||
|
||||
@@ -392,7 +392,7 @@ The [Examples Directory](https://github.com/payloadcms/payload/tree/main/example
|
||||
|
||||
Below are some common troubleshooting tips. To help other developers, please contribute to this section as you troubleshoot your own application.
|
||||
|
||||
##### SendGrid 403 Forbidden Error
|
||||
#### SendGrid 403 Forbidden Error
|
||||
|
||||
- If you are using [SendGrid Link Branding](https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-link-branding) to remove the "via sendgrid.net" part of your email, you must also setup [Domain Authentication](https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication). This means you can only send emails from an address on this domain — so the `from` addresses in your form submission emails **_cannot_** be anything other than `something@your_domain.com`. This means that from `{{email}}` will not work, but `website@your_domain.com` will. You can still send the form's email address in the body of the email.
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ but different parents.
|
||||
with as much detail as possible.
|
||||
</Banner>
|
||||
|
||||
##### Core features
|
||||
## Core features
|
||||
|
||||
- Automatically adds a `parent` relationship field to each document
|
||||
- Allows for parent/child relationships between documents within the same collection
|
||||
|
||||
@@ -27,7 +27,7 @@ Writing plugins is no more complex than writing regular JavaScript. If you know
|
||||
- Integrate all `upload`-enabled collections with a third-party file host like S3 or Cloudinary
|
||||
- Add custom endpoints or GraphQL queries / mutations with any type of custom functionality that you can think of
|
||||
|
||||
### How to install plugins
|
||||
## How to install plugins
|
||||
|
||||
The base Payload config allows for a `plugins` property which takes an `array` of [`Plugins`](https://github.com/payloadcms/payload/blob/main/packages/payload/src/config/types.ts).
|
||||
|
||||
@@ -82,7 +82,7 @@ const config = buildConfig({
|
||||
export default config
|
||||
```
|
||||
|
||||
#### When Plugins are initialized
|
||||
### When Plugins are initialized
|
||||
|
||||
Payload Plugins are executed _after_ the incoming config is validated, but before it is sanitized and had default options merged in.
|
||||
|
||||
@@ -142,7 +142,7 @@ const addLastModified: Plugin = (incomingConfig: Config): Config => {
|
||||
export default addLastModified
|
||||
```
|
||||
|
||||
### Available Plugins
|
||||
## Available Plugins
|
||||
|
||||
You can discover existing plugins by browsing the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin).
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ For example, if you have a page at `/about` and you want to change it to `/about
|
||||
with as much detail as possible.
|
||||
</Banner>
|
||||
|
||||
##### Core features
|
||||
## Core features
|
||||
|
||||
- Adds a `redirects` collection to your config that:
|
||||
- includes a `from` and `to` fields
|
||||
|
||||
@@ -25,7 +25,7 @@ This plugin is a great way to implement a fast, immersive search experience such
|
||||
with as much detail as possible.
|
||||
</Banner>
|
||||
|
||||
##### Core Features
|
||||
## Core Features
|
||||
|
||||
- Automatically adds an indexed `search` collection to your database
|
||||
- Automatically creates, syncs, and deletes search records as you manage your documents
|
||||
|
||||
@@ -10,7 +10,7 @@ keywords: plugins, sentry, error, tracking, monitoring, logging, bug, reporting,
|
||||
|
||||
This plugin allows you to integrate [Sentry](https://sentry.io/) seamlessly with your [Payload](https://github.com/payloadcms/payload) application.
|
||||
|
||||
### What is Sentry?
|
||||
## What is Sentry?
|
||||
|
||||
Sentry is a powerful error tracking and performance monitoring tool that helps developers identify, diagnose, and resolve issues in their applications.
|
||||
|
||||
@@ -20,7 +20,7 @@ Sentry is a powerful error tracking and performance monitoring tool that helps d
|
||||
|
||||
This multi-faceted software offers a range of features that will help you manage errors with greater ease and ultimately ensure your application is running smoothly:
|
||||
|
||||
#### Core Features
|
||||
## Core Features
|
||||
|
||||
- **Error Tracking**: Instantly captures and logs errors as they occur in your application
|
||||
- **Performance Monitoring**: Tracks application performance to identify slowdowns and bottlenecks
|
||||
|
||||
@@ -23,7 +23,7 @@ To help you visualize what your page might look like in a search engine, a previ
|
||||
with as much detail as possible.
|
||||
</Banner>
|
||||
|
||||
##### Core features
|
||||
## Core features
|
||||
|
||||
- Adds a `meta` field group to every SEO-enabled collection or global
|
||||
- Allows you to define custom functions to auto-generate metadata
|
||||
|
||||
@@ -25,7 +25,7 @@ The beauty of this plugin is the entirety of your application's content and busi
|
||||
with as much detail as possible.
|
||||
</Banner>
|
||||
|
||||
##### Core features
|
||||
## Core features
|
||||
|
||||
- Hides your Stripe credentials when shipping SaaS applications
|
||||
- Allows restricted keys through [Payload access control](https://payloadcms.com/docs/access-control/overview)
|
||||
|
||||
@@ -31,14 +31,14 @@ npm script will build both and output these directories.
|
||||
Payload features a suite of security features that you can rely on to strengthen your application's security. When
|
||||
deploying to Production, it's a good idea to double-check that you are making proper use of each of them.
|
||||
|
||||
##### The Secret Key
|
||||
### The Secret Key
|
||||
|
||||
When you initialize Payload, you provide it with a `secret` property. This property should be impossible to guess and
|
||||
extremely difficult for brute-force attacks to crack. Make sure your Production `secret` is a long, complex string. It's
|
||||
often best practice to store it in an `env` file which is not checked into your Git repository, using `dotenv` to supply
|
||||
it to your `payload.init` call.
|
||||
|
||||
##### Double-check and thoroughly test all Access Control
|
||||
### Double-check and thoroughly test all Access Control
|
||||
|
||||
Because _**you**_ are in complete control of who can do what with your data, you should double and triple-check that you
|
||||
wield that power responsibly before deploying to Production.
|
||||
@@ -47,13 +47,14 @@ wield that power responsibly before deploying to Production.
|
||||
<strong>
|
||||
By default, all Access Control functions require that a user is successfully logged in to
|
||||
Payload to create, read, update, or delete data.
|
||||
</strong>{' '}
|
||||
</strong>
|
||||
|
||||
But, if you allow public user registration, for example, you will want to make sure that your
|
||||
access control functions are more strict - permitting <strong>only appropriate users</strong> to
|
||||
perform appropriate actions.
|
||||
</Banner>
|
||||
|
||||
##### Building the Admin panel
|
||||
### Building the Admin panel
|
||||
|
||||
Before running in Production, you need to have built a production-ready copy of the Payload Admin panel. To do this,
|
||||
Payload provides the `build` NPM script. You can use it by adding a `script` to your `package.json` file like this:
|
||||
@@ -75,19 +76,19 @@ Payload provides the `build` NPM script. You can use it by adding a `script` to
|
||||
Then, to build Payload, you would run `npm run build` in your project folder. A production-ready Admin bundle will be
|
||||
created in the `build` directory.
|
||||
|
||||
##### Setting Node to Production
|
||||
### Setting Node to Production
|
||||
|
||||
Make sure you set the environment variable `NODE_ENV` to `production`. Based on this variable, many Node packages
|
||||
automatically optimize themselves. In production, Payload automatically disables
|
||||
the [GraphQL Playground](/docs/graphql/overview#graphql-playground), serves a production-ready version of the Admin
|
||||
panel, and other changes.
|
||||
|
||||
##### Secure Cookie Settings
|
||||
### Secure Cookie Settings
|
||||
|
||||
You should be using an SSL certificate for production Payload instances, which means you
|
||||
can [enable secure cookies](/docs/authentication/config) in your Authentication-enabled Collection configs.
|
||||
|
||||
##### Preventing API Abuse
|
||||
### Preventing API Abuse
|
||||
|
||||
Payload comes with a robust set of built-in anti-abuse measures, such as locking out users after X amount of failed
|
||||
login attempts, request rate limiting, GraphQL query complexity limits, max `depth` settings, and
|
||||
@@ -97,7 +98,7 @@ more. [Click here to learn more](/docs/production/preventing-abuse).
|
||||
|
||||
Payload can be used with any MongoDB compatible database including AWS DocumentDB or Azure Cosmos DB.
|
||||
|
||||
##### Managing MongoDB yourself
|
||||
### Managing MongoDB yourself
|
||||
|
||||
If you are using a [persistent filesystem-based cloud host](#persistent-vs-ephemeral-filesystems) such as
|
||||
a [DigitalOcean Droplet](https://www.digitalocean.com/products/droplets/) or
|
||||
@@ -106,7 +107,7 @@ server, you might opt to install MongoDB directly on that server itself so that
|
||||
With this approach, you can benefit from faster response times, but scaling can become more involved as your app's user
|
||||
base grows.
|
||||
|
||||
##### Letting someone else do it
|
||||
### Letting someone else do it
|
||||
|
||||
Alternatively, you can rely on a third-party MongoDB host such as [MongoDB Atlas](https://www.mongodb.com/). With Atlas
|
||||
or a similar cloud provider, you can trust them to take care of your database's availability, security, redundancy, and
|
||||
@@ -122,13 +123,13 @@ backups.
|
||||
Limitations](https://www.mongodb.com/docs/atlas/reference/free-shared-limitations/?_ga=2.176267877.1329169847.1677683154-860992573.1647438381#operational-limitations).
|
||||
</Banner>
|
||||
|
||||
##### DocumentDB
|
||||
### DocumentDB
|
||||
|
||||
When using AWS DocumentDB, you will need to configure connection options for authentication in the `connectOptions`
|
||||
passed to the `mongooseAdapter` . You also need to set `connectOptions.useFacet` to `false` to disable use of the
|
||||
unsupported `$facet` aggregation.
|
||||
|
||||
##### CosmosDB
|
||||
### CosmosDB
|
||||
|
||||
When using Azure Cosmos DB, an index is needed for any field you may want to sort on. To add the sort index for all
|
||||
fields that may be sorted in the admin UI use the <a href="/docs/configuration/overview">indexSortableFields</a>
|
||||
@@ -140,7 +141,7 @@ If you are using Payload to [manage file uploads](/docs/upload/overview), you ne
|
||||
will be permanently stored. If you do not use Payload for file uploads, then this section does not impact your app
|
||||
whatsoever.
|
||||
|
||||
#### Persistent vs Ephemeral Filesystems
|
||||
### Persistent vs Ephemeral Filesystems
|
||||
|
||||
Some cloud app hosts such as [Heroku](https://heroku.com) use `ephemeral` file systems, which means that any files
|
||||
uploaded to your server only last until the server restarts or shuts down. Heroku and similar providers schedule
|
||||
@@ -169,7 +170,7 @@ perpetually.
|
||||
with a persistent filesystem or have an integration with a third-party file host like Amazon S3.
|
||||
</Banner>
|
||||
|
||||
##### Using ephemeral filesystem providers like Heroku
|
||||
### Using ephemeral filesystem providers like Heroku
|
||||
|
||||
If you don't use Payload's `upload` functionality, you can go ahead and use Heroku or similar platform easily.
|
||||
Everything will work exactly as you want it to.
|
||||
|
||||
@@ -10,11 +10,11 @@ keywords: abuse, production, config, configuration, documentation, Content Manag
|
||||
|
||||
Payload has built-in security best practices that can be configured to your application-specific needs.
|
||||
|
||||
### Limit Failed Login Attempts
|
||||
## Limit Failed Login Attempts
|
||||
|
||||
Set the max number of failed login attempts before a user account is locked out for a period of time. Set the `maxLoginAttempts` on the collections that feature Authentication to a reasonable but low number for your users to get in. Use the `lockTime` to set a number in milliseconds from the time a user fails their last allowed attempt that a user must wait to try again.
|
||||
|
||||
### Rate Limiting Requests
|
||||
## Rate Limiting Requests
|
||||
|
||||
To prevent DDoS, brute-force, and similar attacks, you can set IP-based rate limits so that once a certain threshold of requests has been hit by a single IP, further requests from the same IP will be ignored. The Payload config `rateLimit` property accepts an object with the following properties:
|
||||
|
||||
@@ -35,19 +35,19 @@ To prevent DDoS, brute-force, and similar attacks, you can set IP-based rate lim
|
||||
you set <strong>trustProxy</strong> to <strong>true</strong>.
|
||||
</Banner>
|
||||
|
||||
### Max Depth
|
||||
## Max Depth
|
||||
|
||||
Querying a collection and automatically including related documents via `depth` incurs a performance cost. Also, it's possible that your configs may have circular relationships, meaning scenarios where an infinite amount of relationships might populate back and forth until your server times out and crashes. You can prevent any potential of depth-related issues by setting a `maxDepth` property on your Payload config.. The maximum allowed depth should be as small as possible without interrupting dev experience, and it defaults to `10`.
|
||||
|
||||
### Cross-Site Request Forgery (CSRF)
|
||||
## Cross-Site Request Forgery (CSRF)
|
||||
|
||||
CSRF prevention will verify the authenticity of each request to your API to prevent a malicious action from another site from authorized users. See how to configure CSRF [here](/docs/authentication/overview#csrf-protection).
|
||||
|
||||
### Cross Origin Resource Sharing (CORS)
|
||||
## Cross Origin Resource Sharing (CORS)
|
||||
|
||||
To securely allow headless operation you will need to configure the allowed origins for requests to be able to use the Payload API. You can see how to set CORS as well as other payload configuration settings [here](/docs/configuration/overview)
|
||||
|
||||
### Limiting GraphQL Complexity
|
||||
## Limiting GraphQL Complexity
|
||||
|
||||
Because GraphQL gives the power of query writing outside a server's control, someone with bad intentions might write a maliciously complex query and bog down your server. To prevent resource-intensive GraphQL requests, Payload provides a way specify complexity limits which are based on a complexity score that is calculated for each request.
|
||||
|
||||
@@ -55,7 +55,7 @@ Any GraphQL request that is calculated to be too expensive is rejected. On the P
|
||||
|
||||
If you do not need GraphQL it is advised that you disable it altogether with the Payload config by setting `graphQL.disable: true`. Should you wish to enable GraphQL again, you can remove this property or set it `false`, any time. By turning it off, Payload will bypass creating schemas from your collections and will not register the express route.
|
||||
|
||||
### Malicious File Uploads
|
||||
## Malicious File Uploads
|
||||
|
||||
Payload does not execute uploaded files on the server, but depending on your setup it may be used to transmit and store potentially dangerous files. If your configuration allows file uploads there is the potential that a bad actor uploads a malicious file that is then served to other users. Consider the following ways to mitigate the risks.
|
||||
|
||||
|
||||
@@ -11,12 +11,13 @@ Payload provides an extremely granular querying language through all APIs. Each
|
||||
<Banner>
|
||||
<strong>
|
||||
Here, "querying" relates to filtering or searching through documents within a Collection.
|
||||
</strong>{' '}
|
||||
</strong>
|
||||
|
||||
You can build queries to pass to Find operations as well as to [restrict which documents certain
|
||||
users can access](/docs/access-control/overview) via access control functions.
|
||||
</Banner>
|
||||
|
||||
### Simple queries
|
||||
## Simple queries
|
||||
|
||||
For example, say you have a collection as follows:
|
||||
|
||||
@@ -52,7 +53,7 @@ const query = {
|
||||
|
||||
The above example demonstrates a simple query but you can get much more complex.
|
||||
|
||||
### Operators
|
||||
## Operators
|
||||
|
||||
| Operator | Description |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -77,7 +78,7 @@ The above example demonstrates a simple query but you can get much more complex.
|
||||
</strong> to a field's config which will speed up searches using that field immensely.
|
||||
</Banner>
|
||||
|
||||
### And / Or Logic
|
||||
## And / Or Logic
|
||||
|
||||
In addition to defining simple queries, you can join multiple queries together using simple AND / OR logic. Let's take the above `Post` collection for example and write a more complex query using AND / OR:
|
||||
|
||||
@@ -111,7 +112,7 @@ const query = {
|
||||
|
||||
Written in plain English, if the above query were passed to a `find` operation, it would translate to finding posts where either the `color` is `mint` OR the `color` is `white` AND `featured` is set to false.
|
||||
|
||||
### Nested properties
|
||||
## Nested properties
|
||||
|
||||
When working with nested properties, which can happen when using relational fields, it is possible to use the dot notation to access the nested property. For example, when working with a `Song` collection that has a `artists` field which is related to an `Artists` collection using the `name: 'artists'`. You can access a property within the collection `Artists` like so:
|
||||
|
||||
@@ -124,7 +125,7 @@ const query = {
|
||||
}
|
||||
```
|
||||
|
||||
### GraphQL Find Queries
|
||||
## GraphQL Find Queries
|
||||
|
||||
All GraphQL `find` queries support the `where` argument, which accepts queries exactly as detailed above.
|
||||
|
||||
@@ -141,7 +142,7 @@ query {
|
||||
}
|
||||
```
|
||||
|
||||
### REST Queries
|
||||
## REST Queries
|
||||
|
||||
With the REST API, you can use the full power of Payload queries as well but they become a bit more unwieldy the more complex that they get.
|
||||
|
||||
@@ -177,7 +178,7 @@ const getPosts = async () => {
|
||||
}
|
||||
```
|
||||
|
||||
### Local API Queries
|
||||
## Local API Queries
|
||||
|
||||
The Local API's `find` operation accepts an object exactly how you write it. For example:
|
||||
|
||||
|
||||
@@ -6,656 +6,4 @@ desc: Built by Meta, Lexical is an incredibly powerful rich text editor, and it
|
||||
keywords: lexical, rich text, editor, headless cms
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
<Banner type="warning">
|
||||
Payload's Lexical rich text editor is currently in beta. It's stable enough to use as you build on
|
||||
Payload, so if you're up for helping us fine-tune it, you should use it. But if you're looking for
|
||||
stability, use Slate instead.
|
||||
</Banner>
|
||||
|
||||
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
|
||||
1. A "hover" toolbar that pops up if you select text
|
||||
1. It supports Payload blocks natively, directly within your rich text editor
|
||||
1. Custom elements, called "features", are much easier to build in Lexical vs. Slate
|
||||
|
||||
To use the Lexical editor, first you need to install it:
|
||||
|
||||
```
|
||||
npm install @payloadcms/richtext-lexical
|
||||
```
|
||||
|
||||
Once you have it installed, you can pass it to your top-level Payload config as follows:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
|
||||
export default buildConfig({
|
||||
collections: [
|
||||
// your collections here
|
||||
],
|
||||
// Pass the Lexical editor to the root config
|
||||
editor: lexicalEditor({}),
|
||||
})
|
||||
```
|
||||
|
||||
You can also override Lexical settings on a field-by-field basis as follows:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
// Pass the Lexical editor here and override base settings as necessary
|
||||
editor: lexicalEditor({}),
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Extending the lexical editor with Features
|
||||
|
||||
Lexical has been designed with extensibility in mind. Whether you're aiming to introduce new functionalities or tweak the existing ones, Lexical makes it seamless for you to bring those changes to life.
|
||||
|
||||
### Features: The Building Blocks
|
||||
|
||||
At the heart of Lexical's customization potential are "features". While Lexical ships with a set of default features we believe are essential for most use cases, the true power lies in your ability to redefine, expand, or prune these as needed.
|
||||
|
||||
If you remove all the default features, you're left with a blank editor. You can then add in only the features you need, or you can build your own custom features from scratch.
|
||||
|
||||
### Integrating New Features
|
||||
|
||||
To weave in your custom features, utilize the `features` prop when initializing the Lexical Editor. Here's a basic example of how this is done:
|
||||
|
||||
```ts
|
||||
import {
|
||||
BlocksFeature,
|
||||
LinkFeature,
|
||||
UploadFeature,
|
||||
lexicalEditor,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
import { Banner } from '../blocks/Banner'
|
||||
import { CallToAction } from '../blocks/CallToAction'
|
||||
|
||||
{
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
LinkFeature({
|
||||
// Example showing how to customize the built-in fields
|
||||
// of the Link feature
|
||||
fields: [
|
||||
{
|
||||
name: 'rel',
|
||||
label: 'Rel Attribute',
|
||||
type: 'select',
|
||||
hasMany: true,
|
||||
options: ['noopener', 'noreferrer', 'nofollow'],
|
||||
admin: {
|
||||
description:
|
||||
'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
UploadFeature({
|
||||
collections: {
|
||||
uploads: {
|
||||
// Example showing how to customize the built-in fields
|
||||
// of the Upload feature
|
||||
fields: [
|
||||
{
|
||||
name: 'caption',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor(),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
// This is incredibly powerful. You can re-use your Payload blocks
|
||||
// directly in the Lexical editor as follows:
|
||||
BlocksFeature({
|
||||
blocks: [Banner, CallToAction],
|
||||
}),
|
||||
],
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Features overview
|
||||
|
||||
Here's an overview of all the included features:
|
||||
|
||||
| Feature Name | Included by default | Description |
|
||||
|--------------------------------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`BoldTextFeature`** | Yes | Handles the bold text format |
|
||||
| **`ItalicTextFeature`** | Yes | Handles the italic text format |
|
||||
| **`UnderlineTextFeature`** | Yes | Handles the underline text format |
|
||||
| **`StrikethroughTextFeature`** | Yes | Handles the strikethrough text format |
|
||||
| **`SubscriptTextFeature`** | Yes | Handles the subscript text format |
|
||||
| **`SuperscriptTextFeature`** | Yes | Handles the superscript text format |
|
||||
| **`InlineCodeTextFeature`** | Yes | Handles the inline-code text format |
|
||||
| **`ParagraphFeature`** | Yes | Handles paragraphs. Since they are already a key feature of lexical itself, this Feature mainly handles the Slash and Add-Block menu entries for paragraphs |
|
||||
| **`HeadingFeature`** | Yes | Adds Heading Nodes (by default, H1 - H6, but that can be customized) |
|
||||
| **`AlignFeature`** | Yes | Allows you to align text left, centered and right |
|
||||
| **`IndentFeature`** | Yes | Allows you to indent text with the tab key |
|
||||
| **`UnorderedListFeature`** | Yes | Adds unordered lists (ul) |
|
||||
| **`OrderedListFeature`** | Yes | Adds ordered lists (ol) |
|
||||
| **`CheckListFeature`** | Yes | Adds checklists |
|
||||
| **`LinkFeature`** | Yes | Allows you to create internal and external links |
|
||||
| **`RelationshipFeature`** | Yes | Allows you to create block-level (not inline) relationships to other documents |
|
||||
| **`BlockQuoteFeature`** | Yes | Allows you to create block-level quotes |
|
||||
| **`UploadFeature`** | Yes | Allows you to create block-level upload nodes - this supports all kinds of uploads, not just images |
|
||||
| **`HorizontalRuleFeature`** | Yes | Horizontal rules / separators. Basically displays an <hr> element |
|
||||
| **`BlocksFeature`** | No | Allows you to use Payload's [Blocks Field](/docs/fields/blocks) directly inside your editor. In the feature props, you can specify the allowed blocks - just like in the Blocks field. |
|
||||
| **`TreeViewFeature`** | No | Adds a debug box under the editor, which allows you to see the current editor state live, the dom, as well as time travel. Very useful for debugging |
|
||||
|
||||
## Creating your own, custom Feature
|
||||
|
||||
Creating your own custom feature requires deep knowledge of the Lexical editor. We recommend you take a look at the [Lexical documentation](https://lexical.dev/docs/intro) first - especially the "concepts" section.
|
||||
|
||||
Next, take a look at the [features we've already built](https://github.com/payloadcms/payload/tree/main/packages/richtext-lexical/src/field/features) - understanding how they work will help you understand how to create your own. There is no difference between the features included by default and the ones you create yourself - since those features are all isolated from the "core", you have access to the same APIs, whether the feature is part of payload or not!
|
||||
|
||||
## Converters
|
||||
|
||||
### Lexical => HTML
|
||||
|
||||
Lexical saves data in JSON, but can also generate its HTML representation via two main methods:
|
||||
|
||||
1. **Outputting HTML from the Collection:** Create a new field in your collection to convert saved JSON content to HTML. Payload generates and outputs the HTML for use in your frontend.
|
||||
2. **Generating HTML on any server** Convert JSON to HTML on-demand on the server.
|
||||
|
||||
The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
|
||||
|
||||
#### Outputting HTML from the Collection
|
||||
|
||||
To add HTML generation directly within the collection, follow the example below:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import { HTMLConverterFeature, lexicalEditor, lexicalHTML } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: 'nameOfYourRichTextField',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
// The HTMLConverter Feature is the feature which manages the HTML serializers.
|
||||
// If you do not pass any arguments to it, it will use the default serializers.
|
||||
HTMLConverterFeature({}),
|
||||
],
|
||||
}),
|
||||
},
|
||||
lexicalHTML('nameOfYourRichTextField', { name: 'nameOfYourRichTextField_html' }),
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
The `lexicalHTML()` function creates a new field that automatically converts the referenced lexical richText field into HTML through an afterRead hook.
|
||||
|
||||
#### Generating HTML anywhere on the server:
|
||||
|
||||
If you wish to convert JSON to HTML ad-hoc, use this code snippet:
|
||||
|
||||
```ts
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
import {
|
||||
type SanitizedEditorConfig,
|
||||
convertLexicalToHTML,
|
||||
consolidateHTMLConverters,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
|
||||
async function lexicalToHTML(
|
||||
editorData: SerializedEditorState,
|
||||
editorConfig: SanitizedEditorConfig,
|
||||
) {
|
||||
return await convertLexicalToHTML({
|
||||
converters: consolidateHTMLConverters({ editorConfig }),
|
||||
data: editorData,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This method employs `convertLexicalToHTML` from `@payloadcms/richtext-lexical`, which converts the serialized editor state into HTML.
|
||||
|
||||
Because every `Feature` is able to provide html converters, and because the `htmlFeature` can modify those or provide their own, we need to consolidate them with the default html Converters using the `consolidateHTMLConverters` function.
|
||||
|
||||
#### CSS
|
||||
|
||||
Payload's lexical HTML converter does not generate CSS for you, but it does add classes to the generated HTML. You can use these classes to style the HTML in your frontend.
|
||||
|
||||
Here is some "base" CSS you can use to ensure that nested lists render correctly:
|
||||
|
||||
```css
|
||||
/* Base CSS for Lexical HTML */
|
||||
.nestedListItem, .list-check {
|
||||
list-style-type: none;
|
||||
}
|
||||
```
|
||||
|
||||
#### Creating your own HTML Converter
|
||||
|
||||
HTML Converters are typed as `HTMLConverter`, which contains the node type it should handle, and a function that accepts the serialized node from the lexical editor, and outputs the HTML string. Here's the HTML Converter of the Upload node as an example:
|
||||
|
||||
```ts
|
||||
import type { HTMLConverter } from '@payloadcms/richtext-lexical'
|
||||
import payload from 'payload'
|
||||
|
||||
const UploadHTMLConverter: HTMLConverter<SerializedUploadNode> = {
|
||||
converter: async ({ node }) => {
|
||||
const uploadDocument = await payload.findByID({
|
||||
id: node.value.id,
|
||||
collection: node.relationTo,
|
||||
})
|
||||
const url = (payload?.config?.serverURL || '') + uploadDocument?.url
|
||||
|
||||
if (!(uploadDocument?.mimeType as string)?.startsWith('image')) {
|
||||
// Only images can be serialized as HTML
|
||||
return ``
|
||||
}
|
||||
|
||||
return `<img src="${url}" alt="${uploadDocument?.filename}" width="${uploadDocument?.width}" height="${uploadDocument?.height}"/>`
|
||||
},
|
||||
nodeTypes: [UploadNode.getType()], // This is the type of the lexical node that this converter can handle. Instead of hardcoding 'upload' we can get the node type directly from the UploadNode, since it's static.
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, we have access to all the information saved in the node (for the Upload node, this is `value`and `relationTo`) and we can use that to generate the HTML.
|
||||
|
||||
The `convertLexicalToHTML` is part of `@payloadcms/richtext-lexical` automatically handles traversing the editor state and calling the correct converter for each node.
|
||||
|
||||
#### Embedding the HTML Converter in your Feature
|
||||
|
||||
You can embed your HTML Converter directly within your custom `Feature`, allowing it to be handled automatically by the `consolidateHTMLConverters` function. Here is an example:
|
||||
|
||||
```ts
|
||||
export const UploadFeature = (props?: UploadFeatureProps): FeatureProvider => {
|
||||
return {
|
||||
feature: () => {
|
||||
return {
|
||||
nodes: [
|
||||
{
|
||||
converters: {
|
||||
html: yourHTMLConverter, // <= This is where you define your HTML Converter
|
||||
},
|
||||
node: UploadNode,
|
||||
type: UploadNode.getType(),
|
||||
//...
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
/*...*/
|
||||
],
|
||||
props: props,
|
||||
slashMenu: {
|
||||
/*...*/
|
||||
},
|
||||
}
|
||||
},
|
||||
key: 'upload',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Headless Editor
|
||||
|
||||
Lexical provides a seamless way to perform conversions between various other formats:
|
||||
|
||||
- HTML to Lexical (or, importing HTML into the lexical editor)
|
||||
- Markdown to Lexical (or, importing Markdown into the lexical editor)
|
||||
- Lexical to Markdown
|
||||
|
||||
A headless editor can perform such conversions outside of the main editor instance. Follow this method to initiate a headless editor:
|
||||
|
||||
```ts
|
||||
import { createHeadlessEditor } from '@lexical/headless' // <= make sure this package is installed
|
||||
import { getEnabledNodes, sanitizeEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const yourEditorConfig // <= your editor config here
|
||||
|
||||
const headlessEditor = createHeadlessEditor({
|
||||
nodes: getEnabledNodes({
|
||||
editorConfig: sanitizeEditorConfig(yourEditorConfig),
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
### Getting the editor config
|
||||
|
||||
As you can see, you need to provide an editor config in order to create a headless editor. This is because the editor config is used to determine which nodes & features are enabled, and which converters are used.
|
||||
|
||||
To get the editor config, simply import the default editor config and adjust it - just like you did inside of the `editor: lexicalEditor({})` property:
|
||||
|
||||
```ts
|
||||
import { defaultEditorConfig, defaultEditorFeatures } from '@payloadcms/richtext-lexical' // <= make sure this package is installed
|
||||
|
||||
const yourEditorConfig = defaultEditorConfig
|
||||
|
||||
// If you made changes to the features of the field's editor config, you should also make those changes here:
|
||||
yourEditorConfig.features = [
|
||||
...defaultEditorFeatures,
|
||||
// Add your custom features here
|
||||
]
|
||||
```
|
||||
|
||||
### HTML => Lexical
|
||||
|
||||
Once you have your headless editor instance, you can use it to convert HTML to Lexical:
|
||||
|
||||
```ts
|
||||
import { $generateNodesFromDOM } from '@lexical/html'
|
||||
import { $getRoot, $getSelection } from 'lexical'
|
||||
import { JSDOM } from 'jsdom'
|
||||
|
||||
headlessEditor.update(
|
||||
() => {
|
||||
// In a headless environment you can use a package such as JSDom to parse the HTML string.
|
||||
const dom = new JSDOM(htmlString)
|
||||
|
||||
// Once you have the DOM instance it's easy to generate LexicalNodes.
|
||||
const nodes = $generateNodesFromDOM(headlessEditor, dom.window.document)
|
||||
|
||||
// Select the root
|
||||
$getRoot().select()
|
||||
|
||||
// Insert them at a selection.
|
||||
const selection = $getSelection()
|
||||
selection.insertNodes(nodes)
|
||||
},
|
||||
{ discrete: true },
|
||||
)
|
||||
|
||||
// Do this if you then want to get the editor JSON
|
||||
const editorJSON = headlessEditor.getEditorState().toJSON()
|
||||
```
|
||||
|
||||
Functions prefixed with a `$` can only be run inside of an `editor.update()` or `editorState.read()` callback.
|
||||
|
||||
This has been taken from the [lexical serialization & deserialization docs](https://lexical.dev/docs/concepts/serialization#html---lexical).
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
Using the <code>discrete: true</code> flag ensures instant updates to the editor state. If
|
||||
immediate reading of the updated state isn't necessary, you can omit the flag.
|
||||
</Banner>
|
||||
|
||||
### Markdown => Lexical
|
||||
|
||||
Convert markdown content to the Lexical editor format with the following:
|
||||
|
||||
```ts
|
||||
import { $convertFromMarkdownString } from '@lexical/markdown'
|
||||
import { sanitizeEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const yourSanitizedEditorConfig = sanitizeEditorConfig(yourEditorConfig) // <= your editor config here
|
||||
const markdown = `# Hello World`
|
||||
|
||||
headlessEditor.update(
|
||||
() => {
|
||||
$convertFromMarkdownString(markdown, yourSanitizedEditorConfig.features.markdownTransformers)
|
||||
},
|
||||
{ discrete: true },
|
||||
)
|
||||
|
||||
// Do this if you then want to get the editor JSON
|
||||
const editorJSON = headlessEditor.getEditorState().toJSON()
|
||||
```
|
||||
|
||||
### Lexical => Markdown
|
||||
|
||||
Export content from the Lexical editor into Markdown format using these steps:
|
||||
|
||||
1. Import your current editor state into the headless editor.
|
||||
2. Convert and fetch the resulting markdown string.
|
||||
|
||||
Here's the code for it:
|
||||
|
||||
```ts
|
||||
import { $convertToMarkdownString } from '@lexical/markdown'
|
||||
import { sanitizeEditorConfig } from '@payloadcms/richtext-lexical'
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
|
||||
const yourSanitizedEditorConfig = sanitizeEditorConfig(yourEditorConfig) // <= your editor config here
|
||||
const yourEditorState: SerializedEditorState // <= your current editor state here
|
||||
|
||||
// Import editor state into your headless editor
|
||||
try {
|
||||
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
|
||||
} catch (e) {
|
||||
logger.error({ err: e }, 'ERROR parsing editor state')
|
||||
}
|
||||
|
||||
// Export to markdown
|
||||
let markdown: string
|
||||
headlessEditor.getEditorState().read(() => {
|
||||
markdown = $convertToMarkdownString(yourSanitizedEditorConfig?.features?.markdownTransformers)
|
||||
})
|
||||
```
|
||||
|
||||
The `.setEditorState()` function immediately updates your editor state. Thus, there's no need for the `discrete: true` flag when reading the state afterward.
|
||||
|
||||
### Lexical => Plain Text
|
||||
|
||||
Export content from the Lexical editor into plain text using these steps:
|
||||
|
||||
1. Import your current editor state into the headless editor.
|
||||
2. Convert and fetch the resulting plain text string.
|
||||
|
||||
Here's the code for it:
|
||||
|
||||
```ts
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
import { $getRoot } from 'lexical'
|
||||
|
||||
const yourEditorState: SerializedEditorState // <= your current editor state here
|
||||
|
||||
// Import editor state into your headless editor
|
||||
try {
|
||||
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
|
||||
} catch (e) {
|
||||
logger.error({ err: e }, 'ERROR parsing editor state')
|
||||
}
|
||||
|
||||
// Export to plain text
|
||||
const plainTextContent =
|
||||
headlessEditor.getEditorState().read(() => {
|
||||
return $getRoot().getTextContent()
|
||||
}) || ''
|
||||
```
|
||||
|
||||
## Migrating from Slate
|
||||
|
||||
While both Slate and Lexical save the editor state in JSON, the structure of the JSON is different.
|
||||
|
||||
### Migration via SlateToLexicalFeature
|
||||
|
||||
One way to handle this is to just give your lexical editor the ability to read the slate JSON.
|
||||
|
||||
Simply add the `SlateToLexicalFeature` to your editor:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import { SlateToLexicalFeature, lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: 'nameOfYourRichTextField',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [...defaultFeatures, SlateToLexicalFeature({})],
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
and done! Now, everytime this lexical editor is initialized, it converts the slate date to lexical on-the-fly. If the data is already in lexical format, it will just pass it through.
|
||||
|
||||
This is by far the easiest way to migrate from Slate to Lexical, although it does come with a few caveats:
|
||||
|
||||
- There is a performance hit when initializing the lexical editor
|
||||
- The editor will still output the Slate data in the output JSON, as the on-the-fly converter only runs for the admin panel
|
||||
|
||||
The easy way to solve this: Just save the document! This overrides the slate data with the lexical data, and the next time the document is loaded, the lexical data will be used. This solves both the performance and the output issue for that specific document.
|
||||
|
||||
### Migration via migration script
|
||||
|
||||
The method described above does not solve the issue for all documents, though. If you want to convert all your documents to lexical, you can use a migration script. Here's a simple example:
|
||||
|
||||
```ts
|
||||
import type { Payload } from 'payload'
|
||||
import type { YourDocumentType } from 'payload/generated-types'
|
||||
|
||||
import {
|
||||
cloneDeep,
|
||||
convertSlateToLexical,
|
||||
defaultSlateConverters,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
|
||||
import { AnotherCustomConverter } from './lexicalFeatures/converters/AnotherCustomConverter'
|
||||
|
||||
export async function convertAll(payload: Payload, collectionName: string, fieldName: string) {
|
||||
const docs: YourDocumentType[] = await payload.db.collections[collectionName].find({}).exec() // Use MongoDB models directly to query all documents at once
|
||||
console.log(`Found ${docs.length} ${collectionName} docs`)
|
||||
|
||||
const converters = cloneDeep([...defaultSlateConverters, AnotherCustomConverter])
|
||||
|
||||
// Split docs into batches of 20.
|
||||
const batchSize = 20
|
||||
const batches = []
|
||||
for (let i = 0; i < docs.length; i += batchSize) {
|
||||
batches.push(docs.slice(i, i + batchSize))
|
||||
}
|
||||
|
||||
let processed = 0 // Number of processed docs
|
||||
|
||||
for (const batch of batches) {
|
||||
// Process each batch asynchronously
|
||||
const promises = batch.map(async (doc: YourDocumentType) => {
|
||||
const richText = doc[fieldName]
|
||||
|
||||
if (richText && Array.isArray(richText) && !('root' in richText)) {
|
||||
// It's Slate data - skip already-converted data
|
||||
const converted = convertSlateToLexical({
|
||||
converters: converters,
|
||||
slateData: richText,
|
||||
})
|
||||
|
||||
await payload.update({
|
||||
id: doc.id,
|
||||
collection: collectionName as any,
|
||||
data: {
|
||||
[fieldName]: converted,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Wait for all promises in the batch to complete. Resolving batches of 20 asynchronously is faster than waiting for each doc to update individually
|
||||
await Promise.all(promises)
|
||||
|
||||
// Update the count of processed docs
|
||||
processed += batch.length
|
||||
console.log(`Converted ${processed} of ${docs.length}`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `convertSlateToLexical` is the same method used in the `SlateToLexicalFeature` - it handles traversing the Slate JSON for you.
|
||||
|
||||
Do note that this script might require adjustment depending on your document structure, especially if you have nested richText fields or localization enabled.
|
||||
|
||||
### Converting custom Slate nodes
|
||||
|
||||
If you have custom Slate nodes, create a custom converter for them. Here's the Upload converter as an example:
|
||||
|
||||
```ts
|
||||
import type { SerializedUploadNode } from '../uploadNode.'
|
||||
import type { SlateNodeConverter } from '@payloadcms/richtext-lexical'
|
||||
|
||||
export const SlateUploadConverter: SlateNodeConverter = {
|
||||
converter({ slateNode }) {
|
||||
return {
|
||||
fields: {
|
||||
...slateNode.fields,
|
||||
},
|
||||
format: '',
|
||||
relationTo: slateNode.relationTo,
|
||||
type: 'upload',
|
||||
value: {
|
||||
id: slateNode.value?.id || '',
|
||||
},
|
||||
version: 1,
|
||||
} as const as SerializedUploadNode
|
||||
},
|
||||
nodeTypes: ['upload'],
|
||||
}
|
||||
```
|
||||
|
||||
It's pretty simple: You get a Slate node as input, and you return the lexical node. The `nodeTypes` array is used to determine which Slate nodes this converter can handle.
|
||||
|
||||
When using a migration script, you can add your custom converters to the `converters` property of the `convertSlateToLexical` props, as seen in the example above
|
||||
|
||||
When using the `SlateToLexicalFeature`, you can add your custom converters to the `converters` property of the `SlateToLexicalFeature` props:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import {
|
||||
SlateToLexicalFeature,
|
||||
lexicalEditor,
|
||||
defaultSlateConverters,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
|
||||
import { YourCustomConverter } from '../converters/YourCustomConverter'
|
||||
|
||||
const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: 'nameOfYourRichTextField',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
SlateToLexicalFeature({
|
||||
converters: [...defaultSlateConverters, YourCustomConverter],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Migrating from payload-plugin-lexical
|
||||
|
||||
Migrating from [payload-plugin-lexical](https://github.com/AlessioGr/payload-plugin-lexical) works similar to migrating from Slate.
|
||||
|
||||
Instead of a `SlateToLexicalFeature` there is a `LexicalPluginToLexicalFeature` you can use. And instead of `convertSlateToLexical` you can use `convertLexicalPluginToLexical`.
|
||||
|
||||
## Coming Soon
|
||||
|
||||
Lots more documentation will be coming soon, which will show in detail how to create your own custom features within Lexical.
|
||||
|
||||
For now, take a look at the TypeScript interfaces and let us know if you need a hand. Much more will be coming from the Payload team on this topic soon.
|
||||
The new lexical docs can be found at [Lexical](/docs/lexical/overview).
|
||||
|
||||
@@ -9,7 +9,7 @@ keywords: slatejs, lexical, rich text, json, custom editor, javascript, typescri
|
||||
Payload currently supports two official rich text editors and you can choose either one depending on your needs.
|
||||
|
||||
1. [SlateJS](/docs/rich-text/slate) - stable, backwards-compatible with 1.0
|
||||
2. [Lexical](/docs/rich-text/lexical) - beta, where things will be moving in the future
|
||||
2. [Lexical](/docs/lexical/overview) - beta, where things will be moving in the future
|
||||
|
||||
These editors are built on an "adapter pattern" which means that you will need to install the editor you'd like to use. Take a look at the docs for the editor you'd like to use for instructions on how to install it.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: headless cms, typescript, documentation, Content Management System, cm
|
||||
|
||||
While building your own custom functionality into Payload, like plugins, hooks, access control functions, custom views, GraphQL queries / mutations, or anything else, you may benefit from generating your own TypeScript types dynamically from your Payload config itself.
|
||||
|
||||
### Types generation script
|
||||
## Types generation script
|
||||
|
||||
Run the following command in a Payload project to generate types based on your Payload config:
|
||||
|
||||
@@ -18,7 +18,7 @@ payload generate:types
|
||||
|
||||
You can run this command whenever you need to regenerate your types, and then you can use these types in your Payload code directly.
|
||||
|
||||
### Disable declare statement
|
||||
## Disable declare statement
|
||||
|
||||
By default, `generate:types` will add a `declare` statement to your types file, which automatically enables type inference within Payload.
|
||||
|
||||
@@ -44,7 +44,7 @@ declare module 'payload' {
|
||||
}
|
||||
```
|
||||
|
||||
### Custom output file path
|
||||
## Custom output file path
|
||||
|
||||
You can specify where you want your types to be generated by adding a property to your Payload config:
|
||||
|
||||
@@ -61,7 +61,7 @@ You can specify where you want your types to be generated by adding a property t
|
||||
|
||||
The above example places your types next to your Payload config itself as the file `generated-types.ts`.
|
||||
|
||||
### Example Usage
|
||||
## Example Usage
|
||||
|
||||
For example, let's look at the following simple Payload config:
|
||||
|
||||
@@ -123,7 +123,7 @@ export interface Post {
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Field Interfaces
|
||||
## Custom Field Interfaces
|
||||
|
||||
For `array`, `block`, `group` and named `tab` fields, you can generate top level reusable interfaces. The following group field config:
|
||||
|
||||
@@ -170,11 +170,11 @@ export interface Collection1 {
|
||||
appending the field type to the end, i.e. `MetaGroup` or similar.
|
||||
</Banner>
|
||||
|
||||
### Using your types
|
||||
## Using your types
|
||||
|
||||
Now that your types have been generated, payloads local API will now be typed. It is common for users to want to use this in their frontend code, we recommend generating them with payload and then copying the file over to your frontend codebase. This is the simplest way to get your types into your frontend codebase.
|
||||
|
||||
#### Adding an NPM script
|
||||
### Adding an NPM script
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important</strong>
|
||||
|
||||
@@ -14,7 +14,7 @@ npx create-payload-app@latest
|
||||
|
||||
Pick a TypeScript project type to get started easily.
|
||||
|
||||
#### Setting up from Scratch
|
||||
## Setting up from Scratch
|
||||
|
||||
It's also possible to set up a TypeScript project from scratch. We plan to write up a guide for exactly how—so keep an eye out for that, too.
|
||||
|
||||
@@ -22,14 +22,14 @@ It's also possible to set up a TypeScript project from scratch. We plan to write
|
||||
|
||||
Payload exports a number of types that you may find useful while writing your own plugins, hooks, access control functions, custom views, GraphQL queries / mutations, or anything else.
|
||||
|
||||
##### Config Types
|
||||
## Config Types
|
||||
|
||||
- [Base config](/docs/configuration/overview#typescript)
|
||||
- [Collections](/docs/configuration/collections#typescript)
|
||||
- [Globals](/docs/configuration/globals#typescript)
|
||||
- [Fields](/docs/fields/overview#typescript)
|
||||
|
||||
##### Hook Types
|
||||
## Hook Types
|
||||
|
||||
- [Collection hooks](/docs/hooks/collections#typescript)
|
||||
- [Global hooks](/docs/hooks/globals#typescript)
|
||||
|
||||
@@ -27,7 +27,7 @@ _Admin panel screenshot depicting a Media Collection with Upload enabled_
|
||||
1. The Admin panel will modify its `Edit` view(s) to add a new set of corresponding Upload UI which will allow for file upload
|
||||
1. The `create`, `update`, and `delete` Collection operations will be modified to support file upload, re-upload, and deletion
|
||||
|
||||
### Enabling Uploads
|
||||
## Enabling Uploads
|
||||
|
||||
Every Payload Collection can opt-in to supporting Uploads by specifying the `upload` property on the Collection's config to either `true` or to an object containing `upload` options.
|
||||
|
||||
@@ -38,7 +38,7 @@ Every Payload Collection can opt-in to supporting Uploads by specifying the `upl
|
||||
</strong> on that collection.
|
||||
</Banner>
|
||||
|
||||
### Collection Upload Options
|
||||
## Collection Upload Options
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -104,7 +104,7 @@ export const Media: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Payload-wide Upload Options
|
||||
## Payload-wide Upload Options
|
||||
|
||||
Payload relies on the [`express-fileupload`](https://www.npmjs.com/package/express-fileupload) package to manage file uploads in Express. In addition to the Upload options specifiable on a Collection by Collection basis, you can also control the `express-fileupload` options by passing your base Payload config an `upload` property containing an object supportive of all `express-fileupload` properties which use `Busboy` under the hood. [Click here](https://github.com/mscdex/busboy#api) for more documentation about what you can control.
|
||||
|
||||
@@ -134,7 +134,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Image Sizes
|
||||
## Image Sizes
|
||||
|
||||
If you specify an array of `imageSizes` to your `upload` config, Payload will automatically crop and resize your uploads to fit each of the sizes specified by your config.
|
||||
|
||||
@@ -142,13 +142,13 @@ The Payload Admin panel will also automatically display all available files, inc
|
||||
|
||||
Behind the scenes, Payload relies on [`sharp`](https://sharp.pixelplumbing.com/api-resize#resize) to perform its image resizing. You can specify additional options for `sharp` to use while resizing your images.
|
||||
|
||||
##### Accessing the resized images in hooks
|
||||
#### Accessing the resized images in hooks
|
||||
|
||||
All auto-resized images are exposed to be re-used in hooks and similar via an object that is bound to `req.payloadUploadSizes`.
|
||||
|
||||
The object will have keys for each size generated, and each key will be set equal to a buffer containing the file data.
|
||||
|
||||
##### Handling Image Enlargement
|
||||
#### Handling Image Enlargement
|
||||
|
||||
When an uploaded image is smaller than the defined image size, we have 3 options:
|
||||
|
||||
@@ -163,7 +163,7 @@ When an uploaded image is smaller than the defined image size, we have 3 options
|
||||
image size. Use the `withoutEnlargement` prop to change this.
|
||||
</Banner>
|
||||
|
||||
### Crop and Focal Point Selector
|
||||
## Crop and Focal Point Selector
|
||||
|
||||
This feature is only available for image file types.
|
||||
|
||||
@@ -173,7 +173,7 @@ Image cropping occurs before any resizing, the resized images will therefore be
|
||||
|
||||
If no resizing options are specified (`imageSizes` or `resizeOptions`), the focal point selector will not be displayed.
|
||||
|
||||
### Disabling Local Upload Storage
|
||||
## Disabling Local Upload Storage
|
||||
|
||||
If you are using a plugin to send your files off to a third-party file storage host or CDN, like Amazon S3 or similar, you may not want to store your files locally at all. You can prevent Payload from writing files to disk by specifying `disableLocalStorage: true` on your collection's upload config.
|
||||
|
||||
@@ -186,7 +186,7 @@ If you are using a plugin to send your files off to a third-party file storage h
|
||||
provide your own admin thumbnail using <strong>upload.adminThumbnail</strong>.
|
||||
</Banner>
|
||||
|
||||
### Admin Thumbnails
|
||||
## Admin Thumbnails
|
||||
|
||||
You can specify how Payload retrieves admin thumbnails for your upload-enabled Collections. This property accepts two different configurations:
|
||||
|
||||
@@ -220,7 +220,7 @@ export const Media: CollectionConfig = {
|
||||
the default generic file thumbnail instead.
|
||||
</Banner>
|
||||
|
||||
### MimeTypes
|
||||
## MimeTypes
|
||||
|
||||
Specifying the `mimeTypes` property can restrict what files are allowed from the user's file picker. This accepts an array of strings, which can be any valid mimetype or mimetype wildcards
|
||||
|
||||
@@ -241,7 +241,7 @@ export const Media: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Uploading Files
|
||||
## Uploading Files
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
@@ -256,6 +256,6 @@ Send your request as a `multipart/form-data` request, using [`FormData`](https:/
|
||||
|
||||
[Here is a walkthrough](https://muffinman.io/blog/uploading-files-using-fetch-multipart-form-data/) detailing how to upload files as `multipart/form-data` using React.
|
||||
|
||||
### Access Control
|
||||
## Access Control
|
||||
|
||||
All files that are uploaded to each Collection automatically support the `read` [Access Control](/docs/access-control/overview) function from the Collection itself. You can use this to control who should be allowed to see your uploads, and who should not.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Storage Adapters
|
||||
label: Overview
|
||||
label: Storage Adapters
|
||||
order: 20
|
||||
desc: Payload provides additional storage adapters to handle file uploads. These adapters allow you to store files in different locations, such as Amazon S3, Vercel Blob Storage, Google Cloud Storage, Uploadthing, and more.
|
||||
keywords: uploads, images, media, storage, adapters, s3, vercel, google cloud, azure
|
||||
@@ -16,15 +16,16 @@ Payload offers additional storage adapters to handle file uploads. These adapter
|
||||
| Google Cloud Storage | [`@payloadcms/storage-gcs`](https://github.com/payloadcms/payload/tree/beta/packages/storage-gcs) |
|
||||
|
||||
|
||||
### Vercel Blob Storage [`@payloadcms/storage-vercel-blob`](https://www.npmjs.com/package/@payloadcms/storage-vercel-blob)
|
||||
## Vercel Blob Storage
|
||||
[`@payloadcms/storage-vercel-blob`](https://www.npmjs.com/package/@payloadcms/storage-vercel-blob)
|
||||
|
||||
#### Installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-vercel-blob
|
||||
```
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Vercel Blob adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- Ensure you have `BLOB_READ_WRITE_TOKEN` set in your Vercel environment variables. This is usually set by Vercel automatically after adding blob storage to your project.
|
||||
@@ -54,7 +55,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#### Configuration Options
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
| -------------------- | -------------------------------------------------------------------- | ----------------------------- |
|
||||
@@ -64,15 +65,16 @@ export default buildConfig({
|
||||
| `cacheControlMaxAge` | Cache-Control max-age in seconds | `365 * 24 * 60 * 60` (1 Year) |
|
||||
| `token` | Vercel Blob storage read/write token | `''` |
|
||||
|
||||
### S3 Storage [`@payloadcms/storage-s3`](https://www.npmjs.com/package/@payloadcms/storage-s3)
|
||||
## S3 Storage
|
||||
[`@payloadcms/storage-s3`](https://www.npmjs.com/package/@payloadcms/storage-s3)
|
||||
|
||||
#### Installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-s3
|
||||
```
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Vercel Blob adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- The `config` object can be any [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object (from [`@aws-sdk/client-s3`](https://github.com/aws/aws-sdk-js-v3)). _This is highly dependent on your AWS setup_. Check the AWS documentation for more information.
|
||||
@@ -107,19 +109,20 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
##### Configuration Options
|
||||
#### Configuration Options
|
||||
|
||||
See the the [AWS SDK Package](https://github.com/aws/aws-sdk-js-v3) and [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object for guidance on AWS S3 configuration.
|
||||
|
||||
### Azure Blob Storage - [`@payloadcms/storage-azure`](https://www.npmjs.com/package/@payloadcms/storage-azure)
|
||||
## Azure Blob Storage
|
||||
[`@payloadcms/storage-azure`](https://www.npmjs.com/package/@payloadcms/storage-azure)
|
||||
|
||||
#### Installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-azure
|
||||
```
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Vercel Blob adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
|
||||
@@ -148,7 +151,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#### Configuration Options
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
| ---------------------- | ------------------------------------------------------------------------ | ------- |
|
||||
@@ -159,15 +162,16 @@ export default buildConfig({
|
||||
| `connectionString` | Azure Blob storage connection string | |
|
||||
| `containerName` | Azure Blob storage container name | |
|
||||
|
||||
### Google Cloud Storage [`@payloadcms/storage-gcs`](https://www.npmjs.com/package/@payloadcms/storage-gcs)
|
||||
## Google Cloud Storage
|
||||
[`@payloadcms/storage-gcs`](https://www.npmjs.com/package/@payloadcms/storage-gcs)
|
||||
|
||||
#### Installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-gcs
|
||||
```
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Vercel Blob adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
|
||||
@@ -197,7 +201,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#### Configuration Options
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
| ------------- | --------------------------------------------------------------------------------------------------- | --------- |
|
||||
@@ -208,15 +212,16 @@ export default buildConfig({
|
||||
| `acl` | Access control list for files that are uploaded | `Private` |
|
||||
|
||||
|
||||
### Uploadthing Storage [`@payloadcms/storage-uploadthing`](https://www.npmjs.com/package/@payloadcms/storage-uploadthing)
|
||||
## Uploadthing Storage
|
||||
[`@payloadcms/storage-uploadthing`](https://www.npmjs.com/package/@payloadcms/storage-uploadthing)
|
||||
|
||||
#### Installation
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @paylaodcms/storage-uploadthing
|
||||
```
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use uploadthing. The slug _must_ match one of your existing collection slugs and be an `upload` type.
|
||||
- Get an API key from Uploadthing and set it as `apiKey` in the `options` object.
|
||||
@@ -239,7 +244,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#### Configuration Options
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
| ---------------- | ----------------------------------------------- | ------------- |
|
||||
@@ -250,15 +255,15 @@ export default buildConfig({
|
||||
| `defaultKeyType` | Default key type for file operations | `fileKey` |
|
||||
|
||||
|
||||
### Custom Storage Adapters
|
||||
## Custom Storage Adapters
|
||||
|
||||
If you need to create a custom storage adapter, you can use the [`@payloadcms/plugin-cloud-storage`](https://www.npmjs.com/package/@payloadcms/plugin-cloud-storage) package. This package is used internally by the storage adapters mentioned above.
|
||||
|
||||
#### Installation
|
||||
### Installation
|
||||
|
||||
`pnpm add @payloadcms/plugin-cloud-storage`
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
Reference any of the existing storage adapters for guidance on how this should be structured. Create an adapter following the `GeneratedAdapter` interface. Then, pass the adapter to the `cloudStorage` plugin.
|
||||
|
||||
@@ -298,7 +303,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Plugin options
|
||||
## Plugin options
|
||||
|
||||
This plugin is configurable to work across many different Payload collections. A `*` denotes that the property is required.
|
||||
|
||||
@@ -307,7 +312,7 @@ This plugin is configurable to work across many different Payload collections. A
|
||||
| `collections` \* | `Record<string, CollectionOptions>` | Object with keys set to the slug of collections you want to enable the plugin for, and values set to collection-specific options. |
|
||||
| `enabled` | | `boolean` to conditionally enable/disable plugin. Default: true. |
|
||||
|
||||
### Collection-specific options
|
||||
## Collection-specific options
|
||||
|
||||
| Option | Type | Description |
|
||||
| ----------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -317,7 +322,7 @@ This plugin is configurable to work across many different Payload collections. A
|
||||
| `prefix` | `string` | Set to `media/images` to upload files inside `media/images` folder in the bucket. |
|
||||
| `generateFileURL` | [GenerateFileURL](https://github.com/payloadcms/plugin-cloud-storage/blob/master/src/types.ts#L53) | Override the generated file URL with one that you create. |
|
||||
|
||||
### Payload Access Control
|
||||
## Payload Access Control
|
||||
|
||||
Payload ships with access control that runs _even on statically served files_. The same `read` access control property on your `upload`-enabled collections is used, and it allows you to restrict who can request your uploaded files.
|
||||
|
||||
@@ -327,7 +332,7 @@ Instead, all uploads will still be reached from the default `/collectionSlug/sta
|
||||
|
||||
If this does not apply to you (your upload collection has `read: () => true` or similar) you can disable this functionality by setting `disablePayloadAccessControl` to `true`. When this setting is in place, this plugin will update your file URLs to point directly to your cloud host.
|
||||
|
||||
### Conditionally Enabling/Disabling
|
||||
## Conditionally Enabling/Disabling
|
||||
|
||||
The proper way to conditionally enable/disable this plugin is to use the `enabled` property.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Extending on Payload's [Draft](/docs/versions/drafts) functionality, you can con
|
||||

|
||||
_If Autosave is enabled, drafts will be created automatically as the document is modified and the Admin UI adds an indicator describing when the document was last saved to the top right of the sidebar._
|
||||
|
||||
### Options
|
||||
## Options
|
||||
|
||||
Collections and Globals both support the same options for configuring autosave. You can either set `versions.drafts.autosave` to `true`, or pass an object to configure autosave properties.
|
||||
|
||||
@@ -60,11 +60,11 @@ export const Pages: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Autosave API
|
||||
## Autosave API
|
||||
|
||||
When `autosave` is enabled, all `update` operations within Payload expose a new argument called `autosave`. When set to `true`, Payload will treat the incoming draft update as an `autosave`. This is primarily used by the Admin UI, but there may be some cases where you are building an app for your users and wish to implement `autosave` in your own app. To do so, use the `autosave` argument in your `update` operations.
|
||||
|
||||
#### How autosaves are stored
|
||||
### How autosaves are stored
|
||||
|
||||
If we created a new version for each autosave, you'd quickly find a ton of autosaves that clutter up your `_versions` collection within the database. That would be messy quick because `autosave` is typically set to save a document at ~800ms intervals.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ By enabling Versions with Drafts, your collections and globals can maintain _new
|
||||

|
||||
_If Drafts are enabled, the typical Save button is replaced with new actions which allow you to either save a draft, or publish your changes._
|
||||
|
||||
### Options
|
||||
## Options
|
||||
|
||||
Collections and Globals both support the same options for configuring drafts. You can either set `versions.drafts` to `true`, or pass an object to configure draft properties.
|
||||
|
||||
@@ -23,7 +23,7 @@ Collections and Globals both support the same options for configuring drafts. Yo
|
||||
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `autosave` | Enable `autosave` to automatically save progress while documents are edited. To enable, set to `true` or pass an object with [options](/docs/versions/autosave). |
|
||||
|
||||
### Database changes
|
||||
## Database changes
|
||||
|
||||
By enabling drafts on a collection or a global, Payload will <strong>automatically inject a new field into your schema</strong> called `_status`. The `_status` field is used internally by Payload to store if a document is set to `draft` or `published`.
|
||||
|
||||
@@ -37,7 +37,7 @@ Within the Admin UI, if drafts are enabled, a document can be shown with one of
|
||||
1. <strong>Changed</strong> - if a document has been published, but there are newer drafts available
|
||||
and not yet published
|
||||
|
||||
### Draft API
|
||||
## Draft API
|
||||
|
||||
<Banner type="success">
|
||||
If drafts are enabled on your collection or global, important and powerful changes are made to
|
||||
@@ -45,7 +45,7 @@ Within the Admin UI, if drafts are enabled, a document can be shown with one of
|
||||
with live documents.
|
||||
</Banner>
|
||||
|
||||
##### Updating or creating drafts
|
||||
#### Updating or creating drafts
|
||||
|
||||
If you enable drafts on a collection or global, the `create` and `update` operations for REST, GraphQL, and Local APIs expose a new option called `draft` which allows you to specify if you are creating or updating a <strong>draft</strong>, or if you're just sending your changes straight to the published document. For example, if you pass the query parameter `?draft=true` to a REST `create` or `update` operation, your action will be treated as if you are creating a `draft` and not a published document. By default, the `draft` argument is set to `false`.
|
||||
|
||||
@@ -53,7 +53,7 @@ If you enable drafts on a collection or global, the `create` and `update` operat
|
||||
|
||||
If `draft` is enabled while creating or updating a document, all fields are considered as not required, so that you can save drafts that are incomplete.
|
||||
|
||||
##### Reading drafts vs. published documents
|
||||
#### Reading drafts vs. published documents
|
||||
|
||||
In addition to the `draft` argument within `create` and `update` operations, a `draft` argument is also exposed for `find` and `findByID` operations.
|
||||
|
||||
@@ -71,7 +71,7 @@ If you simply fetch your created document using a `find` or `findByID` operation
|
||||
|
||||
But, if you specify `draft` as `true`, Payload will automatically replace your published document's content with content coming from the most recently saved `version`. In this case, as we have created _two_ versions in the above scenario, Payload will send back data from the newest (second) draft and your document will appear as the most recently drafted version instead of the published version.
|
||||
|
||||
### Controlling who can see Collection drafts
|
||||
## Controlling who can see Collection drafts
|
||||
|
||||
<Banner type="warning">
|
||||
If you're using the <strong>drafts</strong> feature, it's important for you to consider who can
|
||||
@@ -79,7 +79,7 @@ But, if you specify `draft` as `true`, Payload will automatically replace your p
|
||||
simple and puts the power completely in your hands.
|
||||
</Banner>
|
||||
|
||||
##### Restricting draft access
|
||||
#### Restricting draft access
|
||||
|
||||
You can use the `read` [Access Control](/docs/access-control/collections#read) method to restrict who is able to view drafts of your documents by simply returning a [query constraint](/docs/queries/overview) which restricts the documents that any given user is able to retrieve.
|
||||
|
||||
@@ -165,10 +165,10 @@ export const Pages: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### Unpublishing drafts
|
||||
## Unpublishing drafts
|
||||
|
||||
If a document is published, the Payload Admin UI will be updated to show an "unpublish" button at the top of the sidebar, which will "unpublish" the currently published document. Consider this as a way to "revert" a document back to a draft state. On the API side, this is done by simply setting `_status: 'draft'` on any document.
|
||||
|
||||
### Reverting to published
|
||||
## Reverting to published
|
||||
|
||||
If a document is published, and you have made further changes which are saved as a draft, Payload will show a "revert to published" button at the top of the sidebar which will allow you to reject your draft changes and "revert" back to the published state of the document. Your drafts will still be saved, but a new version will be created that will reflect the last published state of the document.
|
||||
|
||||
@@ -31,25 +31,25 @@ _Comparing an old version to a newer version of a document_
|
||||
discretion.
|
||||
</Banner>
|
||||
|
||||
### Options
|
||||
## Options
|
||||
|
||||
Versions support a few different levels of functionality that each come with their own impacts to document workflow.
|
||||
|
||||
##### Versions enabled, drafts disabled
|
||||
### Versions enabled, drafts disabled
|
||||
|
||||
If you enable versions but keep draft mode disabled, Payload will simply create a new version of a document each time you update a document. This is great for use cases where you need to retain a history of all document updates over time, but always want to treat the newest document version as the version that is "published".
|
||||
|
||||
For example, a use case for "versions enabled, drafts disabled" could be on a collection of users, where you might want to keep a version history (or audit log) of all changes ever made to users - but any changes to users should _always_ be treated as "published" and you have no need to maintain a "draft" version of a user.
|
||||
|
||||
##### Versions and drafts enabled
|
||||
### Versions and drafts enabled
|
||||
|
||||
If you have versions _and_ drafts enabled, you are able to control which documents are published, and which are considered draft. That lets you write [access control](/docs/access-control/overview) to control who can see published documents, and who can see draft documents. It also lets you save versions (drafts) that are _newer_ than your most recently published document, which is helpful if you want to draft changes and maybe even preview them before you publish the changes. Read more about Drafts [here](/docs/versions/drafts).
|
||||
|
||||
##### Versions, drafts, and autosave enabled
|
||||
### Versions, drafts, and autosave enabled
|
||||
|
||||
When you have versions, drafts, _and_ `autosave` enabled, the Admin UI will automatically save changes that you make to a new `draft` version as you edit a document, which makes sure that you never lose your changes ever again. Autosave will not affect your published post at all—instead, it'll just save your changes and let you publish them whenever you or your editors are ready to do so. Read more about Autosave [here](/docs/versions/autosave).
|
||||
|
||||
### Collection config
|
||||
## Collection config
|
||||
|
||||
Configuring Versions is done by adding the `versions` key to your Collection configs. Set it to `true` to enable default Versions settings, or customize versions options by setting the property equal to an object containing the following available options:
|
||||
|
||||
@@ -58,7 +58,7 @@ Configuring Versions is done by adding the `versions` key to your Collection con
|
||||
| `maxPerDoc` | Use this setting to control how many versions to keep on a document by document basis. Must be an integer. Defaults to 100, use 0 to save all versions. |
|
||||
| `drafts ` | Enable [Drafts](/docs/versions/drafts) mode for this collection. To enable, set to `true` or pass an object with `draft` [options](/docs/versions/drafts#options). |
|
||||
|
||||
### Global config
|
||||
## Global config
|
||||
|
||||
Global versions work similarly to Collection versions but have a slightly different set of config properties supported.
|
||||
|
||||
@@ -67,7 +67,7 @@ Global versions work similarly to Collection versions but have a slightly differ
|
||||
| `max` | Use this setting to control how many versions to keep on a global by global basis. Must be an integer. |
|
||||
| `drafts` | Enable [Drafts](/docs/versions/drafts) mode for this global. To enable, set to `true` or pass an object with `draft` [options](/docs/versions/drafts#options) |
|
||||
|
||||
#### Database impact
|
||||
### Database impact
|
||||
|
||||
By enabling `versions`, a new database collection will be made to store versions for your collection or global. The collection will be named based off the `slug` of the collection or global and will follow this pattern (where `slug` is replaced with the `slug` of your collection or global):
|
||||
|
||||
@@ -93,7 +93,7 @@ Each document in this new `versions` collection will store a set of meta propert
|
||||
|
||||
Global versions are stored the same as the collection version shown above, except they do not feature the `parent` property, as each Global receives its own `versions` collection. That means we know that all versions in that collection correspond to that specific global.
|
||||
|
||||
### Version operations
|
||||
## Version operations
|
||||
|
||||
Versions expose new operations for both collections and globals. They allow you to find and query versions, find a single version by ID, and publish (or restore) a version by ID. Both Collections and Globals support the same new operations. They are used primarily by the admin UI, but if you are writing custom logic in your app and would like to utilize them, they're available for you to use as well via REST, GraphQL, and Local APIs.
|
||||
|
||||
@@ -120,7 +120,7 @@ Versions expose new operations for both collections and globals. They allow you
|
||||
|
||||
**Collection Local API methods:**
|
||||
|
||||
#### Find
|
||||
### Find
|
||||
|
||||
```js
|
||||
// Result will be a paginated set of Versions.
|
||||
@@ -140,7 +140,7 @@ const result = await payload.findVersions({
|
||||
})
|
||||
```
|
||||
|
||||
#### Find by ID
|
||||
### Find by ID
|
||||
|
||||
```js
|
||||
// Result will be a Post document.
|
||||
@@ -156,7 +156,7 @@ const result = await payload.findVersionByID({
|
||||
})
|
||||
```
|
||||
|
||||
#### Restore
|
||||
### Restore
|
||||
|
||||
```js
|
||||
// Result will be the restored global document.
|
||||
@@ -193,7 +193,7 @@ const result = await payload.restoreVersion({
|
||||
|
||||
**Global Local API methods:**
|
||||
|
||||
#### Find
|
||||
### Find
|
||||
|
||||
```js
|
||||
// Result will be a paginated set of Versions.
|
||||
@@ -213,7 +213,7 @@ const result = await payload.findGlobalVersions({
|
||||
})
|
||||
```
|
||||
|
||||
#### Find by ID
|
||||
### Find by ID
|
||||
|
||||
```js
|
||||
// Result will be a Post document.
|
||||
@@ -229,7 +229,7 @@ const result = await payload.findGlobalVersionByID({
|
||||
})
|
||||
```
|
||||
|
||||
#### Restore
|
||||
### Restore
|
||||
|
||||
```js
|
||||
// Result will be the restored global document.
|
||||
@@ -243,7 +243,7 @@ const result = await payload.restoreGlobalVersion({
|
||||
})
|
||||
```
|
||||
|
||||
### Access Control
|
||||
## Access Control
|
||||
|
||||
Versions expose a new access control function on both Collections and Globals that allow for you to control who can see versions of documents, and who can't.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user