Merge branch 'main' into feat/folders

This commit is contained in:
Jarrod Flesch
2025-04-04 16:22:57 -04:00
524 changed files with 28161 additions and 5724 deletions

View File

@@ -718,7 +718,7 @@ The `useDocumentInfo` hook provides information about the current document being
| **`currentEditor`** | The user currently editing the document. |
| **`docConfig`** | Either the Collection or Global config of the document, depending on what is being edited. |
| **`docPermissions`** | The current document's permissions. Fallback to collection permissions when no id is present. |
| **`documentIsLocked`** | Whether the document is currently locked by another user. [More details](./locked-documents). |
| **`documentIsLocked`** | Whether the document is currently locked by another user. [More details](./locked-documents). |
| **`getDocPermissions`** | Method to retrieve document-level permissions. |
| **`getDocPreferences`** | Method to retrieve document-level user preferences. [More details](./preferences). |
| **`globalSlug`** | The slug of the global if editing a global document. |
@@ -730,7 +730,7 @@ The `useDocumentInfo` hook provides information about the current document being
| **`initialData`** | The initial data of the document. |
| **`isEditing`** | Whether the document is being edited (as opposed to created). |
| **`isInitializing`** | Whether the document info is still initializing. |
| **`isLocked`** | Whether the document is locked. [More details](./locked-documents). |
| **`isLocked`** | Whether the document is locked. [More details](./locked-documents). |
| **`lastUpdateTime`** | Timestamp of the last update to the document. |
| **`mostRecentVersionIsAutosaved`** | Whether the most recent version is an autosaved version. |
| **`preferencesKey`** | The `preferences` key to use when interacting with document-level user preferences. [More details](./preferences). |
@@ -739,9 +739,9 @@ The `useDocumentInfo` hook provides information about the current document being
| **`setDocumentTitle`** | Method to set the document title. |
| **`setHasPublishedDoc`** | Method to update whether the document has been published. |
| **`title`** | The title of the document. |
| **`unlockDocument`** | Method to unlock a document. [More details](./locked-documents). |
| **`unlockDocument`** | Method to unlock a document. [More details](./locked-documents). |
| **`unpublishedVersionCount`** | The number of unpublished versions of the document. |
| **`updateDocumentEditor`** | Method to update who is currently editing the document. [More details](./locked-documents). |
| **`updateDocumentEditor`** | Method to update who is currently editing the document. [More details](./locked-documents). |
| **`updateSavedDocumentData`** | Method to update the saved document data. |
| **`uploadStatus`** | Status of any uploads in progress ('idle', 'uploading', or 'failed'). |
| **`versionCount`** | The current version count of the document. |
@@ -791,17 +791,18 @@ const MyComponent: React.FC = () => {
The `useListQuery` hook returns an object with the following properties:
| Property | Description |
| ------------------------- | ------------------------------------------------------------------------ |
| **`data`** | The data that is being displayed in the List View. |
| **`defaultLimit`** | The default limit of items to display in the List View. |
| **`defaultSort`** | The default sort order of items in the List View. |
| **`handlePageChange`** | A method to handle page changes in the List View. |
| **`handlePerPageChange`** | A method to handle per page changes in the List View. |
| **`handleSearchChange`** | A method to handle search changes in the List View. |
| **`handleSortChange`** | A method to handle sort changes in the List View. |
| **`handleWhereChange`** | A method to handle where changes in the List View. |
| **`query`** | The current query that is being used to fetch the data in the List View. |
| Property | Description |
| ------------------------- | -------------------------------------------------------------------------------------- |
| **`data`** | The data that is being displayed in the List View. |
| **`defaultLimit`** | The default limit of items to display in the List View. |
| **`defaultSort`** | The default sort order of items in the List View. |
| **`handlePageChange`** | A method to handle page changes in the List View. |
| **`handlePerPageChange`** | A method to handle per page changes in the List View. |
| **`handleSearchChange`** | A method to handle search changes in the List View. |
| **`handleSortChange`** | A method to handle sort changes in the List View. |
| **`handleWhereChange`** | A method to handle where changes in the List View. |
| **`modified`** | Whether the query has been changed from its [Query Preset](../query-presets/overview). |
| **`query`** | The current query that is being used to fetch the data in the List View. |
## useSelection

View File

@@ -60,29 +60,31 @@ export const Posts: CollectionConfig = {
The following options are available:
| Option | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `admin` | The configuration options for the Admin Panel. [More details](#admin-options). |
| `access` | Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |
| `auth` | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |
| `custom` | Extension point for adding custom data (e.g. for plugins) |
| `disableDuplicate` | When true, do not show the "Duplicate" button while editing documents within this Collection and prevent `duplicate` from all APIs. |
| `defaultSort` | Pass a top-level field to sort by default in the Collection List View. Prefix the name of the field with a minus symbol ("-") to sort in descending order. Multiple fields can be specified by using a string array. |
| `dbName` | Custom table or Collection name depending on the Database Adapter. Auto-generated from slug if not defined. |
| `endpoints` | Add custom routes to the REST API. Set to `false` to disable routes. [More details](../rest-api/overview#custom-endpoints). |
| `fields` \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [More details](../fields/overview). |
| `graphQL` | Manage GraphQL-related properties for this collection. [More](#graphql) |
| `hooks` | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |
| `labels` | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
| `lockDocuments` | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |
| `slug` \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
| `timestamps` | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
| `typescript` | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| `upload` | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
| `defaultPopulate` | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |
| `indexes` | Define compound indexes for this collection. This can be used to either speed up querying/sorting by 2 or more fields at the same time or to ensure uniqueness between several fields. |
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
| Option | Description |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `admin` | The configuration options for the Admin Panel. [More details](#admin-options). |
| `access` | Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |
| `auth` | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |
| `custom` | Extension point for adding custom data (e.g. for plugins) |
| `disableDuplicate` | When true, do not show the "Duplicate" button while editing documents within this Collection and prevent `duplicate` from all APIs. |
| `defaultSort` | Pass a top-level field to sort by default in the Collection List View. Prefix the name of the field with a minus symbol ("-") to sort in descending order. Multiple fields can be specified by using a string array. |
| `dbName` | Custom table or Collection name depending on the Database Adapter. Auto-generated from slug if not defined. |
| `endpoints` | Add custom routes to the REST API. Set to `false` to disable routes. [More details](../rest-api/overview#custom-endpoints). |
| `fields` \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [More details](../fields/overview). |
| `graphQL` | Manage GraphQL-related properties for this collection. [More](#graphql) |
| `hooks` | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |
| `orderable` | If true, enables custom ordering for the collection, and documents can be reordered via drag and drop. Uses [fractional indexing](https://observablehq.com/@dgreensp/implementing-fractional-indexing) for efficient reordering. |
| `labels` | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
| `enableQueryPresets` | Enable query presets for this Collection. [More details](../query-presets/overview). |
| `lockDocuments` | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |
| `slug` \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
| `timestamps` | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
| `typescript` | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| `upload` | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
| `defaultPopulate` | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |
| `indexes` | Define compound indexes for this collection. This can be used to either speed up querying/sorting by 2 or more fields at the same time or to ensure uniqueness between several fields. |
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
_\* An asterisk denotes that a property is required._
@@ -176,7 +178,7 @@ The following options are available:
```ts
import type { CollectionCOnfig } from 'payload'
export const MyCollection: CollectionCOnfig = {
export const MyCollection: CollectionConfig = {
// ...
admin: {
components: {

View File

@@ -84,6 +84,7 @@ The following options are available:
| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/cookies#csrf-attacks). |
| **`defaultDepth`** | If a user does not specify `depth` while requesting a resource, this depth will be used. [More details](../queries/depth). |
| **`defaultMaxTextLength`** | The maximum allowed string length to be permitted application-wide. Helps to prevent malicious public document creation. |
| `queryPresets` | An object that to configure Collection Query Presets. [More details](../query-presets/overview). |
| **`maxDepth`** | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. [More details](../queries/depth). |
| **`indexSortableFields`** | Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. |
| **`upload`** | Base Payload upload configuration. [More details](../upload/overview#payload-wide-upload-options). |
@@ -146,7 +147,7 @@ _\* Config location detection is different between development and production en
<Banner type="warning">
**Important:** Ensure your `tsconfig.json` is properly configured for Payload
to auto-detect your config location. If if does not exist, or does not specify
to auto-detect your config location. If it does not exist, or does not specify
the proper `compilerOptions`, Payload will default to the current working
directory.
</Banner>
@@ -238,9 +239,9 @@ export default buildConfig({
// ...
// highlight-start
cors: {
origins: ['http://localhost:3000']
headers: ['x-custom-header']
}
origins: ['http://localhost:3000'],
headers: ['x-custom-header'],
},
// highlight-end
})
```

View File

@@ -55,7 +55,7 @@ For more granular control, pass a configuration object instead. Payload exposes
| `exact` | Boolean. When true, will only match if the path matches the `usePathname()` exactly. |
| `strict` | When true, a path that has a trailing slash will only match a `location.pathname` with a trailing slash. This has no effect when there are additional URL segments in the pathname. |
| `sensitive` | When true, will match if the path is case sensitive. |
| `meta` | Page metadata overrides to apply to this view within the Admin Panel. [More details](./metadata). |
| `meta` | Page metadata overrides to apply to this view within the Admin Panel. [More details](../admin/metadata). |
_\* An asterisk denotes that a property is required._

View File

@@ -6,13 +6,13 @@ desc:
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
The List View is where users interact with a list of [Collection](../collections/overview) Documents within the [Admin Panel](../admin/overview). This is where they can view, sort, filter, and paginate their documents to find exactly what they're looking for. This is also where users can perform bulk operations on multiple documents at once, such as deleting, editing, or publishing many.
The List View is where users interact with a list of [Collection](../configuration/collections) Documents within the [Admin Panel](../admin/overview). This is where they can view, sort, filter, and paginate their documents to find exactly what they're looking for. This is also where users can perform bulk operations on multiple documents at once, such as deleting, editing, or publishing many.
The List View can be swapped out in its entirety for a Custom View, or it can be injected with a number of Custom Components to add additional functionality or presentational elements without replacing the entire view.
<Banner type="info">
**Note:** Only [Collections](../collections/overview) have a List View.
[Globals](../globals/overview) do not have a List View as they are single
**Note:** Only [Collections](../configuration/collections) have a List View.
[Globals](../configuration/globals) do not have a List View as they are single
documents.
</Banner>
@@ -90,11 +90,11 @@ The following options are available:
| Path | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `beforeList` | An array of custom components to inject before the list of documents in the List View. [More details](#beforeList). |
| `beforeListTable` | An array of custom components to inject before the table of documents in the List View. [More details](#beforeListTable). |
| `afterList` | An array of custom components to inject after the list of documents in the List View. [More details](#afterList). |
| `afterListTable` | An array of custom components to inject after the table of documents in the List View. [More details](#afterListTable). |
| `Description` | A component to render a description of the Collection. [More details](#Description). |
| `beforeList` | An array of custom components to inject before the list of documents in the List View. [More details](#beforelist). |
| `beforeListTable` | An array of custom components to inject before the table of documents in the List View. [More details](#beforelisttable). |
| `afterList` | An array of custom components to inject after the list of documents in the List View. [More details](#afterlist). |
| `afterListTable` | An array of custom components to inject after the table of documents in the List View. [More details](#afterlisttable). |
| `Description` | A component to render a description of the Collection. [More details](#description). |
### beforeList

View File

@@ -138,6 +138,7 @@ powerful Admin UI.
| **`name`** \* | To be used as the property name when retrieved from the database. [More](./overview#field-names) |
| **`collection`** \* | The `slug`s having the relationship field or an array of collection slugs. |
| **`on`** \* | The name of the relationship or upload field that relates to the collection document. Use dot notation for nested paths, like 'myGroup.relationName'. If `collection` is an array, this field must exist for all specified collections |
| **`orderable`** | If true, enables custom ordering and joined documents can be reordered via drag and drop. Uses [fractional indexing](https://observablehq.com/@dgreensp/implementing-fractional-indexing) for efficient reordering. |
| **`where`** | A `Where` query to hide related documents from appearing. Will be merged with any `where` specified in the request. |
| **`maxDepth`** | Default is 1, Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](../queries/depth#max-depth). |
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |

View File

@@ -28,7 +28,7 @@ Then, you could configure two different runner strategies:
As mentioned above, you can queue jobs, but the jobs won't run unless a worker picks up your jobs and runs them. This can be done in four ways:
#### Cron jobs
### Cron jobs
You can use the `jobs.autoRun` property to configure cron jobs:
@@ -63,7 +63,7 @@ export default buildConfig({
and should not be used on serverless platforms like Vercel.
</Banner>
#### Endpoint
### Endpoint
You can execute jobs by making a fetch request to the `/api/payload-jobs/run` endpoint:
@@ -130,7 +130,7 @@ This works because Vercel automatically makes the `CRON_SECRET` environment vari
After the project is deployed to Vercel, the Vercel Cron job will automatically trigger the `/api/payload-jobs/run` endpoint in the specified schedule, running the queued jobs in the background.
#### Local API
### Local API
If you want to process jobs programmatically from your server-side code, you can use the Local API:
@@ -156,7 +156,7 @@ const results = await payload.jobs.runByID({
})
```
#### Bin script
### Bin script
Finally, you can process jobs via the bin script that comes with Payload out of the box.
@@ -169,3 +169,76 @@ In addition, the bin script allows you to pass a `--cron` flag to the `jobs:run`
```sh
npx payload jobs:run --cron "*/5 * * * *"
```
## Processing Order
By default, jobs are processed first in, first out (FIFO). This means that the first job added to the queue will be the first one processed. However, you can also configure the order in which jobs are processed.
### Jobs Configuration
You can configure the order in which jobs are processed in the jobs configuration by passing the `processingOrder` property. This mimics the Payload [sort](../queries/sort) property that's used for functionality such as `payload.find()`.
```ts
export default buildConfig({
// Other configurations...
jobs: {
tasks: [
// your tasks here
],
processingOrder: '-createdAt', // Process jobs in reverse order of creation = LIFO
},
})
```
You can also set this on a queue-by-queue basis:
```ts
export default buildConfig({
// Other configurations...
jobs: {
tasks: [
// your tasks here
],
processingOrder: {
default: 'createdAt', // FIFO
queues: {
nightly: '-createdAt', // LIFO
myQueue: '-createdAt', // LIFO
},
},
},
})
```
If you need even more control over the processing order, you can pass a function that returns the processing order - this function will be called every time a queue starts processing jobs.
```ts
export default buildConfig({
// Other configurations...
jobs: {
tasks: [
// your tasks here
],
processingOrder: ({ queue }) => {
if (queue === 'myQueue') {
return '-createdAt' // LIFO
}
return 'createdAt' // FIFO
},
},
})
```
### Local API
You can configure the order in which jobs are processed in the `payload.jobs.queue` method by passing the `processingOrder` property.
```ts
const createdJob = await payload.jobs.queue({
workflow: 'createPostAndUpdate',
input: {
title: 'my title',
},
processingOrder: '-createdAt', // Process jobs in reverse order of creation = LIFO
})
```

View File

@@ -55,18 +55,9 @@ Because _**you**_ are in complete control of who can do what with your data, you
wield that power responsibly before deploying to Production.
<Banner type="error">
**
By default, all Access Control functions require that a user is successfully logged in to
Payload to create, read, update, or delete data.
**
But, if you allow public user registration, for example, you will want to make sure that your
access control functions are more strict - permitting
**By default, all Access Control functions require that a user is successfully logged in to Payload to create, read, update, or delete data.**
**
only appropriate users
**
to perform appropriate actions.
But, if you allow public user registration, for example, you will want to make sure that your access control functions are more strict - permitting **only appropriate users** to perform appropriate actions.
</Banner>

View File

@@ -0,0 +1,171 @@
---
title: Query Presets
label: Overview
order: 10
desc: Query Presets allow you to save and share filters, columns, and sort orders for your collections.
keywords:
---
Query Presets allow you to save and share filters, columns, and sort orders for your [Collections](../configuration/collections). This is useful for reusing common or complex filtering patterns and/or sharing them across your team.
Each Query Preset is saved as a new record in the database under the `payload-query-presets` collection. This allows for an endless number of preset configurations, where the users of your app define the presets that are most useful to them, rather than being hard coded into the Payload Config.
Within the [Admin Panel](../admin/overview), Query Presets are applied to the List View. When enabled, new controls are displayed for users to manage presets. Once saved, these presets can be loaded up at any time and optionally shared with others.
To enable Query Presets on a Collection, use the `enableQueryPresets` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
// highlight-start
enableQueryPresets: true,
// highlight-end
}
```
## Config Options
While not required, you may want to customize the behavior of Query Presets to suit your needs, such as add custom labels or access control rules.
Settings for Query Presets are managed on the `queryPresets` property at the root of your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
// highlight-start
queryPresets: {
// ...
},
// highlight-end
})
```
The following options are available for Query Presets:
| Option | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `access` | Used to define custom collection-level access control that applies to all presets. [More details](#access-control). |
| `constraints` | Used to define custom document-level access control that apply to individual presets. [More details](#document-access-control). |
| `labels` | Custom labels to use for the Query Presets collection. |
## Access Control
Query Presets are subject to the same [Access Control](../access-control/overview) as the rest of Payload. This means you can use the same patterns you are already familiar with to control who can read, update, and delete presets.
Access Control for Query Presets can be customized in two ways:
1. [Collection Access Control](#static-access-control): Applies to all presets. These rules are not controllable by the user and are statically defined in the config.
2. [Document Access Control](#dynamic-access-control): Applies to each individual preset. These rules are controllable by the user and are saved to the document.
### Collection Access Control
Collection-level access control applies to _all_ presets within the Query Presets collection. Users cannot control these rules, they are written statically in your config.
To add Collection Access Control, use the `queryPresets.access` property in your [Payload Config](../configuration/overview):
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
queryPresets: {
// ...
// highlight-start
access: {
read: ({ req: { user } }) =>
user ? user?.roles?.some((role) => role === 'admin') : false,
update: ({ req: { user } }) =>
user ? user?.roles?.some((role) => role === 'admin') : false,
},
// highlight-end
},
})
```
This example restricts all Query Presets to users with the role of `admin`.
<Banner type="warning">
**Note:** Custom access control will override the defaults on this collection,
including the requirement for a user to be authenticated. Be sure to include
any necessary checks in your custom rules unless you intend on making these
publicly accessible.
</Banner>
### Document Access Control
You can also define access control rules that apply to each specific preset. Users have the ability to define and modify these rules on the fly as they manage presets. These are saved dynamically in the database on each document.
When a user manages a preset, document-level access control options will be available to them in the Admin Panel for each operation.
By default, Payload provides a set of sensible defaults for all Query Presets, but you can customize these rules to suit your needs:
- **Only Me**: Only the user who created the preset can read, update, and delete it.
- **Everyone**: All users can read, update, and delete the preset.
- **Specific Users**: Only select users can read, update, and delete the preset.
#### Custom Access Control
You can augment the default access control rules with your own custom rules. This can be useful for creating more complex access control patterns that the defaults don't provide, such as for RBAC.
Adding custom access control rules requires:
1. A label to display in the dropdown
2. A set of fields to conditionally render when that option is selected
3. A function that returns the access control rules for that option
To do this, use the `queryPresets.constraints` property in your [Payload Config](../configuration/payload-config).
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
queryPresets: {
// ...
// highlight-start
constraints: {
read: {
label: 'Specific Roles',
value: 'specificRoles',
fields: [
{
name: 'roles',
type: 'select',
hasMany: true,
options: [
{ label: 'Admin', value: 'admin' },
{ label: 'User', value: 'user' },
],
},
],
access: ({ req: { user } }) => ({
'access.read.roles': {
in: [user?.roles],
},
}),
},
// highlight-end
},
},
})
```
In this example, we've added a new option called `Specific Roles` that allows users to select from a list of roles. When this option is selected, the user will be prompted to select one or more roles from a list of options. The access control rule for this option is that the user operating on the preset must have one of the selected roles.
<Banner type="warning">
**Note:** Payload places your custom fields into the `access[operation]` field
group, so your rules will need to reflect this.
</Banner>
The following options are available for each constraint:
| Option | Description |
| -------- | ------------------------------------------------------------------------ |
| `label` | The label to display in the dropdown for this constraint. |
| `value` | The value to store in the database when this constraint is selected. |
| `fields` | An array of fields to render when this constraint is selected. |
| `access` | A function that determines the access control rules for this constraint. |

View File

@@ -21,7 +21,7 @@ import {
// Your richtext data here
const data: SerializedEditorState = {}
const html = convertLexicalToMarkdown({
const markdown = convertLexicalToMarkdown({
data,
editorConfig: await editorConfigFactory.default({
config, // <= make sure you have access to your Payload Config
@@ -101,7 +101,7 @@ import {
editorConfigFactory,
} from '@payloadcms/richtext-lexical'
const html = convertMarkdownToLexical({
const lexicalJSON = convertMarkdownToLexical({
editorConfig: await editorConfigFactory.default({
config, // <= make sure you have access to your Payload Config
}),

View File

@@ -409,7 +409,7 @@ Explore the APIs available through ClientFeature to add the specific functionali
### Adding a client feature to the server feature
Inside of your server feature, you can provide an [import path](/docs/admin/custom-components/overview#component-paths) to the client feature like this:
Inside of your server feature, you can provide an [import path](/docs/custom-components/overview#component-paths) to the client feature like this:
```ts
import { createServerFeature } from '@payloadcms/richtext-lexical'