376 lines
19 KiB
Plaintext
376 lines
19 KiB
Plaintext
---
|
|
title: Swap in your own React components
|
|
label: Custom Components
|
|
order: 20
|
|
desc: Fully customize your Admin Panel by swapping in your own React components. Add fields, remove views, update routes and change functions to sculpt your perfect Dashboard.
|
|
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, express
|
|
---
|
|
|
|
While designing the Payload Admin panel, we determined it should be as minimal and straightforward as possible to allow easy customization and control. There are many times where you may want to completely control how a whole view or a field works. You might even want to add in your own routes entirely. In order for Payload to support that level of customization without introducing versioning / future-proofing issues, Payload provides for a pattern to supply your own React components via your Payload config.
|
|
|
|
To swap in your own React component, first, consult the list of available component overrides below. Determine the scope that corresponds to what you are trying to accomplish, and then author your React component accordingly.
|
|
|
|
<Banner type="success">
|
|
<strong>Tip:</strong><br />
|
|
Custom components will automatically be provided with all props that the default component would accept.
|
|
</Banner>
|
|
|
|
### 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 and mobile Nav in its entirety. |
|
|
| **`logout.Button`** | A custom React component. |
|
|
| **`BeforeDashboard`** | Array of components to inject into the built-in Dashboard, _before_ the default dashboard contents. |
|
|
| **`AfterDashboard`** | Array of components to inject into the built-in Dashboard, _after_ the default dashboard contents. [Demo](https://github.com/payloadcms/payload/tree/master/test/admin/components/AfterDashboard/index.tsx) |
|
|
| **`BeforeLogin`** | Array of components to inject into the built-in Login, _before_ the default login form. |
|
|
| **`AfterLogin`** | Array of components to inject into the built-in Login, _after_ the default login form. |
|
|
| **`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. |
|
|
| **`views.Account`** | The Account view is used to show the currently logged in user's Account page. |
|
|
| **`views.Dashboard`** | The main landing page of the Admin panel. |
|
|
| **`graphics.Icon`** | Used as a graphic within the `Nav` component. Often represents a condensed version of a full logo. |
|
|
| **`graphics.Logo`** | The full logo to be used in contexts like the `Login` view. |
|
|
| **`routes`** | Define your own routes to add to the Payload Admin UI. [More](#custom-routes) |
|
|
| **`providers`** | Define your own provider components that will wrap the Payload Admin UI. [More](#custom-providers) |
|
|
|
|
#### Full example:
|
|
|
|
`payload.config.js`
|
|
|
|
```ts
|
|
import { buildConfig } from "payload/config";
|
|
import {
|
|
MyCustomNav,
|
|
MyCustomLogo,
|
|
MyCustomIcon,
|
|
MyCustomAccount,
|
|
MyCustomDashboard,
|
|
MyProvider,
|
|
} from "./customComponents";
|
|
|
|
export default buildConfig({
|
|
admin: {
|
|
components: {
|
|
Nav: MyCustomNav,
|
|
graphics: {
|
|
Icon: MyCustomIcon,
|
|
Logo: MyCustomLogo,
|
|
},
|
|
views: {
|
|
Account: MyCustomAccount,
|
|
Dashboard: MyCustomDashboard,
|
|
},
|
|
providers: [MyProvider],
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
_For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/master/test/admin/components)._
|
|
|
|
### Collections
|
|
|
|
You can override components on a Collection-by-Collection basis via each Collection's `admin` property.
|
|
|
|
| Path | Description |
|
|
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
| **`views.Edit`** | Used while a document within this Collection is being edited. |
|
|
| **`views.List`** | The `List` view is used to render a paginated, filterable table of Documents in this Collection. |
|
|
| **`edit.SaveButton`** | Replace the default `Save` button with a custom component. Drafts must be disabled |
|
|
| **`edit.SaveDraftButton`** | Replace the default `Save Draft` button with a custom component. Drafts must be enabled and autosave must be disabled. |
|
|
| **`edit.PublishButton`** | Replace the default `Publish` button with a custom component. Drafts must be enabled. |
|
|
| **`edit.PreviewButton`** | Replace the default `Preview` button with a custom component. |
|
|
| **`BeforeList`** | Array of components to inject _before_ the built-in List view |
|
|
| |
|
|
| **`BeforeListTable`** | Array of components to inject _before_ the built-in List view's table |
|
|
| |
|
|
| **`AfterListTable`** | Array of components to inject _after_ the built-in List view's table |
|
|
| |
|
|
| **`AfterList`** | Array of components to inject _after_ the built-in List view |
|
|
| |
|
|
|
|
#### Examples
|
|
|
|
```tsx
|
|
// Custom Buttons
|
|
|
|
import * as React from "react";
|
|
import {
|
|
CustomSaveButtonProps,
|
|
CustomSaveDraftButtonProps,
|
|
CustomPublishButtonProps,
|
|
CustomPreviewButtonProps,
|
|
} from "payload/types";
|
|
|
|
export const CustomSaveButton: CustomSaveButtonProps = ({
|
|
DefaultButton,
|
|
label,
|
|
}) => {
|
|
return <DefaultButton label={label} />;
|
|
};
|
|
|
|
export const CustomSaveDraftButton: CustomSaveDraftButtonProps = ({
|
|
DefaultButton,
|
|
disabled,
|
|
label,
|
|
saveDraft,
|
|
}) => {
|
|
return (
|
|
<DefaultButton label={label} disabled={disabled} saveDraft={saveDraft} />
|
|
);
|
|
};
|
|
|
|
export const CustomPublishButton: CustomPublishButtonProps = ({
|
|
DefaultButton,
|
|
disabled,
|
|
label,
|
|
publish,
|
|
}) => {
|
|
return <DefaultButton label={label} disabled={disabled} publish={publish} />;
|
|
};
|
|
|
|
export const CustomPreviewButton: CustomPreviewButtonProps = ({
|
|
DefaultButton,
|
|
disabled,
|
|
label,
|
|
preview,
|
|
}) => {
|
|
return <DefaultButton label={label} disabled={disabled} preview={preview} />;
|
|
};
|
|
```
|
|
|
|
##### Custom Collection List View Example
|
|
|
|
Collection.ts
|
|
|
|
```tsx
|
|
import { MyListComponent } from './MyListComponent';
|
|
export const MyCollection: CollectionConfig = {
|
|
slug: 'mycollection',
|
|
admin: {
|
|
components: {
|
|
views: {
|
|
List: MyListComponent,
|
|
},
|
|
},
|
|
},
|
|
fields: [
|
|
...
|
|
],
|
|
};
|
|
```
|
|
|
|
MyListComponent.tsx
|
|
|
|
```tsx
|
|
import React from "react";
|
|
import { List, type Props } from "payload/components/views/List"; // Payload's default List view component and its props
|
|
export const MyListComponent: React.FC<Props> = (props) => (
|
|
<div>
|
|
<p>
|
|
Some text before the default list view component. If you just want to do
|
|
that, you can also use the admin.components.list.BeforeList hook
|
|
</p>
|
|
<List {...props} />
|
|
</div>
|
|
);
|
|
```
|
|
|
|
### Globals
|
|
|
|
As with Collections, You can override components on a global-by-global basis via their `admin` property.
|
|
|
|
| Path | Description |
|
|
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
| **`views.Edit`** | Used while this Global is being edited. |
|
|
| **`edit.SaveButton`** | Replace the default `Save` button with a custom component. Drafts must be disabled |
|
|
| **`edit.SaveDraftButton`** | Replace the default `Save Draft` button with a custom component. Drafts must be enabled and autosave must be disabled. |
|
|
| **`edit.PublishButton`** | Replace the default `Publish` button with a custom component. Drafts must be enabled. |
|
|
| **`edit.PreviewButton`** | Replace the default `Preview` button with a custom component. |
|
|
|
|
### 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.
|
|
|
|
<Banner type="success">
|
|
<strong>Tip:</strong>
|
|
<br />
|
|
Don't see a built-in field type that you need? Build it! Using a combination
|
|
of custom validation and custom components, you can override the entirety of
|
|
how a component functions within the admin panel and effectively create your
|
|
own field type.
|
|
</Banner>
|
|
|
|
**Fields support the following custom components:**
|
|
|
|
| Component | Description |
|
|
| ------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
|
| **`Filter`** | Override the text input that is presented in the `List` view when a user is filtering documents by the customized field. |
|
|
| **`Cell`** | Used in the `List` view's table to represent a table-based preview of the data stored in the field. [More](#cell-component) |
|
|
| **`Field`** | Swap out the field itself within all `Edit` views. [More](#field-component) |
|
|
|
|
## Cell Component
|
|
|
|
These are the props that will be passed to your custom Cell to use in your own components.
|
|
|
|
| Property | Description |
|
|
| ---------------- | ----------------------------------------------------------------- |
|
|
| **`field`** | An object that includes the field configuration. |
|
|
| **`colIndex`** | A unique number for the column in the list. |
|
|
| **`collection`** | An object with the config of the collection that the field is in. |
|
|
| **`cellData`** | The data for the field that the cell represents. |
|
|
| **`rowData`** | An object with all the field values for the row. |
|
|
|
|
#### Example
|
|
|
|
```tsx
|
|
import React from "react";
|
|
import "./index.scss";
|
|
const baseClass = "custom-cell";
|
|
|
|
const CustomCell: React.FC<Props> = (props) => {
|
|
const { field, colIndex, collection, cellData, rowData } = props;
|
|
|
|
return <span className={baseClass}>{cellData}</span>;
|
|
};
|
|
```
|
|
|
|
## Field Component
|
|
|
|
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
|
|
|
|
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:
|
|
|
|
```tsx
|
|
import { useField } from "payload/components/forms";
|
|
|
|
type Props = { path: string };
|
|
|
|
const CustomTextField: React.FC<Props> = ({ path }) => {
|
|
// highlight-start
|
|
const { value, setValue } = useField<Props>({ path });
|
|
// highlight-end
|
|
|
|
return (
|
|
<input onChange={(e) => setValue(e.target.value)} value={value.path} />
|
|
);
|
|
};
|
|
```
|
|
|
|
<Banner type="success">
|
|
For more information regarding the hooks that are available to you while you
|
|
build custom components, including the <strong>useField</strong> hook, [click
|
|
here](/docs/admin/hooks).
|
|
</Banner>
|
|
|
|
## Custom routes
|
|
|
|
You can easily add your own custom routes to the Payload Admin panel using the `admin.components.routes` property. Payload currently uses the extremely powerful React Router v5.x and custom routes support all the properties of the React Router `<Route />` component.
|
|
|
|
**Custom routes support the following properties:**
|
|
|
|
| Property | Description |
|
|
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
| **`Component`** \* | Pass in the component that should be rendered when a user navigates to this route. |
|
|
| **`path`** \* | React Router `path`. [See the React Router docs](https://v5.reactrouter.com/web/api/Route/path-string-string) for more info. |
|
|
| **`exact`** | React Router `exact` property. [More](https://v5.reactrouter.com/web/api/Route/exact-bool) |
|
|
| **`strict`** | React Router `strict` property. [More](https://v5.reactrouter.com/web/api/Route/strict-bool) |
|
|
| **`sensitive`** | React Router `sensitive` property. [More](https://v5.reactrouter.com/web/api/Route/sensitive-bool) |
|
|
|
|
_\* An asterisk denotes that a property is required._
|
|
|
|
#### Custom route components
|
|
|
|
Your custom route components will be given all the props that a React Router `<Route />` typically would receive, as well as two props from Payload:
|
|
|
|
| Prop | Description |
|
|
| ----------------------- | ---------------------------------------------------------------------------- |
|
|
| **`user`** | The currently logged in user. Will be `null` if no user is logged in. |
|
|
| **`canAccessAdmin`** \* | If the currently logged in user is allowed to access the admin panel or not. |
|
|
|
|
<Banner type="warning">
|
|
<strong>Note:</strong>
|
|
<br />
|
|
It's up to you to secure your custom routes. If your route requires a user to
|
|
be logged in or to have certain access rights, you should handle that within
|
|
your route component yourself.
|
|
</Banner>
|
|
|
|
#### Example
|
|
|
|
You can find examples of custom route views in the [Payload source code `/test/admin/components/views` folder](https://github.com/payloadcms/payload/tree/master/test/admin/components/views). There, you'll find two custom routes:
|
|
|
|
1. A custom view that uses the `DefaultTemplate`, which is the built-in Payload template that displays the sidebar and "eyebrow nav"
|
|
1. A custom view that uses the `MinimalTemplate` - which is just a centered template used for things like logging in or out
|
|
|
|
To see how to pass in your custom views to create custom routes of your own, take a look at the `admin.components.routes` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/master/test/admin/config.ts).
|
|
|
|
## Custom providers
|
|
|
|
As your admin customizations gets more complex you may want to share state between fields or other components. You can add custom providers to do add your own context to any Payload app for use in other custom components within the admin panel. Within your config add `admin.components.providers`, these can be used to share context or provide other custom functionality. Read the [React context](https://reactjs.org/docs/context.html) docs to learn more.
|
|
|
|
<Banner type="warning">
|
|
<strong>Reminder:</strong> Don't forget to pass the **children** prop through
|
|
the provider component for the admin UI to show
|
|
</Banner>
|
|
|
|
### 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.
|
|
|
|
To make use of Payload SCSS variables / mixins to use directly in your own components, you can import them as follows:
|
|
|
|
```
|
|
@import '~payload/scss';
|
|
```
|
|
|
|
### 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 `react-i18next` in your components.
|
|
|
|
For example:
|
|
|
|
```tsx
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
const CustomComponent: React.FC = () => {
|
|
// highlight-start
|
|
const { t, i18n } = useTranslation("namespace1");
|
|
// highlight-end
|
|
|
|
return (
|
|
<ul>
|
|
<li>{t("key", { variable: "value" })}</li>
|
|
<li>{t("namespace2:key", { variable: "value" })}</li>
|
|
<li>{i18n.language}</li>
|
|
</ul>
|
|
);
|
|
};
|
|
```
|
|
|
|
### Getting the current locale
|
|
|
|
In any custom component you can get the selected locale with the `useLocale` hook. Here is a simple example:
|
|
|
|
```tsx
|
|
import { useLocale } from "payload/components/utilities";
|
|
|
|
const Greeting: React.FC = () => {
|
|
// highlight-start
|
|
const locale = useLocale();
|
|
// highlight-end
|
|
|
|
const trans = {
|
|
en: "Hello",
|
|
es: "Hola",
|
|
};
|
|
|
|
return <span> {trans[locale]} </span>;
|
|
};
|
|
```
|