This PR makes three major changes to the codebase: 1. [Component Paths](#component-paths) Instead of importing custom components into your config directly, they are now defined as file paths and rendered only when needed. That way the Payload config will be significantly more lightweight, and ensures that the Payload config is 100% server-only and Node-safe. Related discussion: https://github.com/payloadcms/payload/discussions/6938 2. [Client Config](#client-config) Deprecates the component map by merging its logic into the client config. The main goal of this change is for performance and simplification. There was no need to deeply iterate over the Payload config twice, once for the component map, and another for the client config. Instead, we can do everything in the client config one time. This has also dramatically simplified the client side prop drilling through the UI library. Now, all components can share the same client config which matches the exact shape of their Payload config (with the exception of non-serializable props and mapped custom components). 3. [Custom client component are no longer server-rendered](#custom-client-components-are-no-longer-server-rendered) Previously, custom components would be server-rendered, no matter if they are server or client components. Now, only server components are rendered on the server. Client components are automatically detected, and simply get passed through as `MappedComponent` to be rendered fully client-side. ## Component Paths Instead of importing custom components into your config directly, they are now defined as file paths and rendered only when needed. That way the Payload config will be significantly more lightweight, and ensures that the Payload config is 100% server-only and Node-safe. Related discussion: https://github.com/payloadcms/payload/discussions/6938 In order to reference any custom components in the Payload config, you now have to specify a string path to the component instead of importing it. Old: ```ts import { MyComponent2} from './MyComponent2.js' admin: { components: { Label: MyComponent2 }, }, ``` New: ```ts admin: { components: { Label: '/collections/Posts/MyComponent2.js#MyComponent2', // <= has to be a relative path based on a baseDir configured in the Payload config - NOT relative based on the importing file }, }, ``` ### Local API within Next.js routes Previously, if you used the Payload Local API within Next.js pages, all the client-side modules are being added to the bundle for that specific page, even if you only need server-side functionality. This `/test` route, which uses the Payload local API, was previously 460 kb. It is now down to 91 kb and does not bundle the Payload client-side admin panel anymore. All tests done [here](https://github.com/payloadcms/payload-3.0-demo/tree/feat/path-test) with beta.67/PR, db-mongodb and default richtext-lexical: **dev /admin before:**  **dev /admin after:**  --- **dev /test before:**  **dev /test after:**  --- **build before:**  **build after::**  ### Usage of the Payload Local API / config outside of Next.js This will make it a lot easier to use the Payload config / local API in other, server-side contexts. Previously, you might encounter errors due to client files (like .scss files) not being allowed to be imported. ## Client Config Deprecates the component map by merging its logic into the client config. The main goal of this change is for performance and simplification. There was no need to deeply iterate over the Payload config twice, once for the component map, and another for the client config. Instead, we can do everything in the client config one time. This has also dramatically simplified the client side prop drilling through the UI library. Now, all components can share the same client config which matches the exact shape of their Payload config (with the exception of non-serializable props and mapped custom components). This is breaking change. The `useComponentMap` hook no longer exists, and most component props have changed (for the better): ```ts const { componentMap } = useComponentMap() // old const { config } = useConfig() // new ``` The `useConfig` hook has also changed in shape, `config` is now a property _within_ the context obj: ```ts const config = useConfig() // old const { config } = useConfig() // new ``` ## Custom Client Components are no longer server rendered Previously, custom components would be server-rendered, no matter if they are server or client components. Now, only server components are rendered on the server. Client components are automatically detected, and simply get passed through as `MappedComponent` to be rendered fully client-side. The benefit of this change: Custom client components can now receive props. Previously, the only way for them to receive dynamic props from a parent client component was to use hooks, e.g. `useFieldProps()`. Now, we do have the option of passing in props to the custom components directly, if they are client components. This will be simpler than having to look for the correct hook. This makes rendering them on the client a little bit more complex, as you now have to check if that component is a server component (=> already has been rendered) or a client component (=> not rendered yet, has to be rendered here). However, this added complexity has been alleviated through the easy-to-use `<RenderMappedComponent />` helper. This helper now also handles rendering arrays of custom components (e.g. beforeList, beforeLogin ...), which actually makes rendering custom components easier in some cases. ## Misc improvements This PR includes misc, breaking changes. For example, we previously allowed unions between components and config object for the same property. E.g. for the custom view property, you were allowed to pass in a custom component or an object with other properties, alongside a custom component. Those union types are now gone. You can now either pass an object, or a component. The previous `{ View: MyViewComponent}` is now `{ View: { Component: MyViewComponent} }` or `{ View: { Default: { Component: MyViewComponent} } }`. This dramatically simplifies the way we read & process those properties, especially in buildComponentMap. We can now simply check for the existence of one specific property, which always has to be a component, instead of running cursed runtime checks on a shared union property which could contain a component, but could also contain functions or objects.   - [x] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository. --------- Co-authored-by: PatrikKozak <patrik@payloadcms.com> Co-authored-by: Paul <paul@payloadcms.com> Co-authored-by: Paul Popus <paul@nouance.io> Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com> Co-authored-by: James <james@trbl.design>
366 lines
21 KiB
Plaintext
366 lines
21 KiB
Plaintext
---
|
||
title: The Admin Panel
|
||
label: Overview
|
||
order: 10
|
||
desc: Manage your data and customize the Payload Admin Panel by swapping in your own React components. Create, modify or remove views, fields, styles and much more.
|
||
keywords: admin, components, custom, customize, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||
---
|
||
|
||
Payload dynamically generates a beautiful, [fully type-safe](../typescript/overview) Admin Panel to manage your users and data. It is highly performant, even with 100+ fields, and is translated in over 30 languages. Within the Admin Panel you can manage content, [render your site](../live-preview/overview), preview drafts, [diff versions](../versions/overview), and so much more.
|
||
|
||
The Admin Panel is designed to [white-label your brand](https://payloadcms.com/blog/white-label-admin-ui). You can endlessly customize and extend the Admin UI by swapping in your own [Custom Components](./components)—everything from simple field labels to entire views can be modified or replaced to perfectly tailor the interface for your editors.
|
||
|
||
The Admin Panel is written in [TypeScript](https://www.typescriptlang.org) and built with [React](https://react.dev) using the [Next.js App Router](https://nextjs.org/docs/app). It supports [React Server Components](https://react.dev/reference/rsc/server-components), enabling the use of the [Local API](/docs/local-api/overview) on the front-end. You can install Payload into any [existing Next.js app in just one line](../getting-started/installation) and [deploy it anywhere](../production).
|
||
|
||
<Banner type="success">
|
||
The Payload Admin Panel is designed to be as minimal and straightforward as possible to allow easy customization and control. [Learn more](./components).
|
||
</Banner>
|
||
|
||
<LightDarkImage
|
||
srcLight="https://payloadcms.com/images/docs/admin.jpg"
|
||
srcDark="https://payloadcms.com/images/docs/admin-dark.jpg"
|
||
alt="Admin Panel with collapsible sidebar"
|
||
caption="Redesigned Admin Panel with a collapsible sidebar that's open by default, providing greater extensibility and enhanced horizontal real estate."
|
||
/>
|
||
|
||
## Project Structure
|
||
|
||
The Admin Panel serves as the entire HTTP layer for Payload, providing a full CRUD interface for your app. This means that both the [REST](../rest-api/overview) and [GraphQL](../graphql/overview) APIs are simply [Next.js Routes](https://nextjs.org/docs/app/building-your-application/routing) that exist directly alongside your front-end application.
|
||
|
||
Once you [install Payload](../getting-started/installation), the following files and directories will be created in your app:
|
||
|
||
```plaintext
|
||
app/
|
||
├─ (payload)/
|
||
├── admin/
|
||
├─── [[...segments]]/
|
||
├──── page.tsx
|
||
├──── not-found.tsx
|
||
├── api/
|
||
├─── [...slug]/
|
||
├──── route.ts
|
||
├── graphql/
|
||
├──── route.ts
|
||
├── graphql-playground/
|
||
├──── route.ts
|
||
├── custom.scss
|
||
├── layout.tsx
|
||
```
|
||
|
||
<Banner type="info">
|
||
If you are not familiar with Next.js project structure, you can [learn more about it here](https://nextjs.org/docs/getting-started/project-structure).
|
||
</Banner>
|
||
|
||
As shown above, all Payload routes are nested within the `(payload)` route group. This creates a boundary between the Admin Panel and the rest of your application by scoping all layouts and styles. The `layout.tsx` file within this directory, for example, is where Payload manages the `html` tag of the document to set proper [`lang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) and [`dir`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) attributes, etc.
|
||
|
||
The `admin` directory contains all the _pages_ related to the interface itself, whereas the `api` and `graphql` directories contains all the _routes_ related to the [REST API](../rest-api/overview) and [GraphQL API](../graphql/overview). All admin routes are [easily configurable](#customizing-routes) to meet your application's exact requirements.
|
||
|
||
<Banner type="warning">
|
||
<strong>Note:</strong>
|
||
If you don't use the [REST API](../rest/overview) or [GraphQL API](../graphql/overview), you can delete the [Next.js files corresponding to those routes](../admin/overview#project-structure), however, the overhead of this API is completely constrained to these endpoints, and will not slow down or affect Payload outside of the endpoints.
|
||
</Banner>
|
||
|
||
Finally, the `custom.scss` file is where you can add or override globally-oriented styles in the Admin Panel, such as modify the color palette. Customizing the look and feel through CSS alone is a powerful feature of the Admin Panel, [more on that here](./customizing-css).
|
||
|
||
All auto-generated files will contain the following comments at the top of each file:
|
||
|
||
```tsx
|
||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */,
|
||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||
```
|
||
|
||
## Defining Custom Components in the Payload Config
|
||
|
||
In the Payload Config, you can define custom React Components to enhance the admin interface. However, these components should not be imported directly into the server-only Payload Config to avoid including client-side code. Instead, you specify the path to the component. Here’s how you can do it:
|
||
|
||
|
||
src/components/Logout.tsx
|
||
```tsx
|
||
'use client'
|
||
import React from 'react'
|
||
|
||
export const MyComponent = () => {
|
||
return (
|
||
<button>Click me!</button>
|
||
)
|
||
}
|
||
```
|
||
|
||
payload.config.ts:
|
||
```ts
|
||
import { buildConfig } from 'payload'
|
||
|
||
const config = buildConfig({
|
||
// ...
|
||
admin: { // highlight-line
|
||
components: {
|
||
logout: {
|
||
Button: '/src/components/Logout#MyComponent'
|
||
}
|
||
}
|
||
},
|
||
})
|
||
```
|
||
|
||
In the path `/src/components/Logout#MyComponent`, `/src/components/Logout` is the file path, and `MyComponent` is the named export. If the component is the default export, the export name can be omitted. Path and export name are separated by a `#`.
|
||
|
||
### Configuring the Base Directory
|
||
|
||
Component paths, by default, are relative to your working directory - this is usually where your Next.js config lies. To simplify component paths, you have the option to configure the *base directory* using the `admin.baseDir.baseDir` property:
|
||
|
||
```ts
|
||
import { buildConfig } from 'payload'
|
||
import { fileURLToPath } from 'node:url'
|
||
import path from 'path'
|
||
const filename = fileURLToPath(import.meta.url)
|
||
const dirname = path.dirname(filename)
|
||
|
||
const config = buildConfig({
|
||
// ...
|
||
admin: { // highlight-line
|
||
importMap: {
|
||
baseDir: path.resolve(dirname, 'src'),
|
||
},
|
||
components: {
|
||
logout: {
|
||
Button: '/components/Logout#MyComponent'
|
||
}
|
||
}
|
||
},
|
||
})
|
||
```
|
||
|
||
In this example, we set the base directory to the `src` directory - thus we can omit the `/src/` part of our component path string.
|
||
|
||
### Passing Props
|
||
|
||
Each React Component in the Payload Config is typed as `PayloadComponent`. This usually is a string, but can also be an object containing the following properties:
|
||
|
||
| Property | Description |
|
||
|---------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||
| `clientProps` | Props to be passed to the React Component if it's a Client Component |
|
||
| `exportName` | Instead of declaring named exports using `#` in the component path, you can also omit them from `path` and pass them in here. |
|
||
| `path` | Path to the React Component. Named exports can be appended to the end of the path, separated by a `#` |
|
||
| `serverProps` | Props to be passed to the React Component if it's a Server Component |
|
||
|
||
To pass in props from the config, you can use the `clientProps` and/or `serverProps` properties. This alleviates the need to use an HOC (Higher-Order-Component) to declare a React Component with props passed in.
|
||
|
||
Here is an example:
|
||
|
||
src/components/Logout.tsx
|
||
```tsx
|
||
'use client'
|
||
import React from 'react'
|
||
|
||
export const MyComponent = ({ text }: { text: string }) => {
|
||
return (
|
||
<button>Click me! {text}</button>
|
||
)
|
||
}
|
||
```
|
||
|
||
payload.config.ts:
|
||
```ts
|
||
import { buildConfig } from 'payload'
|
||
|
||
const config = buildConfig({
|
||
// ...
|
||
admin: { // highlight-line
|
||
components: {
|
||
logout: {
|
||
Button: {
|
||
path: '/src/components/Logout',
|
||
clientProps: {
|
||
text: 'Some Text.'
|
||
},
|
||
exportName: 'MyComponent'
|
||
}
|
||
}
|
||
}
|
||
},
|
||
})
|
||
```
|
||
|
||
### Import Maps
|
||
|
||
It's essential to understand how `PayloadComponent` paths function behind the scenes. Directly importing React Components into your Payload Config using import statements can introduce client-only modules like CSS into your server-only config. This could error when attempting to load the Payload Config in server-only environments and unnecessarily increase the size of the Payload Config, which should remain streamlined and efficient for server use.
|
||
|
||
Instead, we utilize component paths to reference React Components. This method enhances the Payload Config with actual React Component imports on the client side, without affecting server-side usage. A script is deployed to scan the Payload Config, collecting all component paths and creating an `importMap.js`. This file, located in app/(payload)/admin/importMap.js, must be statically imported by your Next.js root page and layout. The script imports all the React Components from the specified paths into a Map, associating them with their respective paths (the ones you defined).
|
||
|
||
When constructing the `ClientConfig`, Payload uses the component paths as keys to fetch the corresponding React Component imports from the Import Map. It then substitutes the `PayloadComponent` with a `MappedComponent`. A `MappedComponent` includes the React Component and additional metadata, such as whether it's a server or a client component and which props it should receive. These components are then rendered through the `<RenderComponent />` component within the Payload Admin Panel.
|
||
|
||
Import maps are regenerated whenever you modify any element related to component paths. This regeneration occurs at startup and whenever Hot Module Replacement (HMR) runs. If the import maps fail to regenerate during HMR, you can restart your application and execute the `payload generate:importmap` command to manually create a new import map.
|
||
|
||
## Admin Options
|
||
|
||
All options for the Admin Panel are defined in your [Payload Config](../configuration/overview) under the `admin` property:
|
||
|
||
```ts
|
||
import { buildConfig } from 'payload'
|
||
|
||
const config = buildConfig({
|
||
// ...
|
||
admin: { // highlight-line
|
||
// ...
|
||
},
|
||
})
|
||
```
|
||
|
||
The following options are available:
|
||
|
||
| Option | Description |
|
||
|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
|
||
| `autoLogin` | Used to automate log-in for dev and demonstration convenience. [More details](../authentication/overview). |
|
||
| `buildPath` | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||
| `components` | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |
|
||
| `custom` | Any custom properties you wish to pass to the Admin Panel. |
|
||
| `dateFormat` | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
|
||
| `disable` | If set to `true`, the entire Admin Panel will be disabled. |
|
||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||
| `meta` | Base metadata to use for the Admin Panel. Included properties are `titleSuffix`, `icons`, and `openGraph`. Can be overridden on a per Collection or per Global basis. |
|
||
| `routes` | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
|
||
| `user` | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
|
||
|
||
<Banner type="success">
|
||
<strong>Reminder:</strong>
|
||
These are the _root-level_ options for the Admin Panel. You can also customize [Collection Admin Options](./collections) and [Global Admin Options](./globals) through their respective `admin` keys.
|
||
</Banner>
|
||
|
||
### The Admin User Collection
|
||
|
||
To specify which Collection to allow to login to the Admin Panel, pass the `admin.user` key equal to the slug of any auth-enabled Collection:
|
||
|
||
```ts
|
||
import { buildConfig } from 'payload'
|
||
|
||
const config = buildConfig({
|
||
// ...
|
||
admin: {
|
||
user: 'admins', // highlight-line
|
||
},
|
||
})
|
||
```
|
||
|
||
<Banner type="warning">
|
||
<strong>Important:</strong>
|
||
<br />
|
||
The Admin Panel can only be used by a single auth-enabled Collection. To enable authentication for a Collection, simply set `auth: true` in the Collection's configuration. See [Authentication](../authentication/overview) for more information.
|
||
</Banner>
|
||
|
||
By default, if you have not specified a Collection, Payload will automatically provide a `User` Collection with access to the Admin Panel. You can customize or override the fields and settings of the default `User` Collection by adding your own Collection with `slug: 'users'`. Doing this will force Payload to use your provided `User` Collection instead of its default version.
|
||
|
||
You can use whatever Collection you'd like to access the Admin Panel as long as the Collection supports [Authentication](/docs/authentication/overview). It doesn't need to be called `users`. For example, you may wish to have two Collections that both support authentication:
|
||
|
||
- `admins` - meant to have a higher level of permissions to manage your data and access the Admin Panel
|
||
- `customers` - meant for end users of your app that should not be allowed to log into the Admin Panel
|
||
|
||
To do this, specify `admin: { user: 'admins' }` in your config. This will provide access to the Admin Panel to only `admins`. Any users authenticated as `customers` will be prevented from accessing the Admin Panel. See [Access Control](/docs/access-control/overview) for full details.
|
||
|
||
### Role-based Access Control
|
||
|
||
It is also possible to allow multiple user types into the Admin Panel with limited permissions, known as role-based access control (RBAC). For example, you may wish to have two roles within the `admins` Collection:
|
||
|
||
- `super-admin` - full access to the Admin Panel to perform any action
|
||
- `editor` - limited access to the Admin Panel to only manage content
|
||
|
||
To do this, add a `roles` or similar field to your auth-enabled Collection, then use the `access.admin` property to grant or deny access based on the value of that field. See [Access Control](/docs/access-control/overview) for full details. For a complete, working example of role-based access control, check out the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth/payload).
|
||
|
||
## Customizing Routes
|
||
|
||
You have full control over the routes that Payload binds itself to. This includes both [Root-level Routes](#root-level-routes) such as the [REST API](../rest-api/overview), and [Admin-level Routes](#admin-level-routes) such as the user's account page. You can customize these routes to meet the needs of your application simply by specifying the desired paths in your config.
|
||
|
||
### Root-level Routes
|
||
|
||
Root-level routes are those that are not behind the `/admin` path, such as the [REST API](../rest-api/overview) and [GraphQL API](../graphql/overview), or the root path of the Admin Panel itself.
|
||
|
||
To customize root-level routes, use the `routes` property in your [Payload Config](../configuration/overview):
|
||
|
||
```ts
|
||
import { buildConfig } from 'payload'
|
||
|
||
const config = buildConfig({
|
||
// ...
|
||
routes: {
|
||
admin: '/custom-admin-route' // highlight-line
|
||
}
|
||
})
|
||
```
|
||
|
||
The following options are available:
|
||
|
||
| Option | Default route | Description |
|
||
|---------------------|-----------------------|---------------------------------------------------|
|
||
| `admin` | `/admin` | The Admin Panel itself. |
|
||
| `api` | `/api` | The [REST API](../rest-api/overview) base path. |
|
||
| `graphQL` | `/graphql` | The [GraphQL API](../graphql/overview) base path. |
|
||
| `graphQLPlayground` | `/graphql-playground` | The GraphQL Playground. |
|
||
|
||
<Banner type="success">
|
||
<strong>Tip:</strong>
|
||
You can easily add _new_ routes to the Admin Panel through [Custom Endpoints](../rest-api/overview#custom-endpoints) and [Custom Views](./views).
|
||
</Banner>
|
||
|
||
#### Customizing Root-level Routes
|
||
|
||
You can change the Root-level Routes as needed, such as to mount the Admin Panel at the root of your application.
|
||
|
||
Changing Root-level Routes also requires a change to [Project Structure](#project-structure) to match the new route. For example, if you set `routes.admin` to `/`, you would need to completely remove the `admin` directory from the project structure:
|
||
|
||
```plaintext
|
||
app/
|
||
├─ (payload)/
|
||
├── [[...segments]]/
|
||
├──── ...
|
||
```
|
||
|
||
<Banner type="warning">
|
||
<strong>Note:</strong>
|
||
If you set Root-level Routes _before_ auto-generating the Admin Panel, your [Project Structure](#project-structure) will already be set up correctly.
|
||
</Banner>
|
||
|
||
### Admin-level Routes
|
||
|
||
Admin-level routes are those behind the `/admin` path. These are the routes that are part of the Admin Panel itself, such as the user's account page, the login page, etc.
|
||
|
||
To customize admin-level routes, use the `admin.routes` property in your [Payload Config](../configuration/overview):
|
||
|
||
```ts
|
||
import { buildConfig } from 'payload'
|
||
|
||
const config = buildConfig({
|
||
// ...
|
||
admin: {
|
||
routes: {
|
||
account: '/my-account' // highlight-line
|
||
}
|
||
},
|
||
})
|
||
```
|
||
|
||
The following options are available:
|
||
|
||
| Option | Default route | Description |
|
||
| ----------------- | ----------------------- | ----------------------------------------------- |
|
||
| `account` | `/account` | The user's account page. |
|
||
| `createFirstUser` | `/create-first-user` | The page to create the first user. |
|
||
| `forgot` | `/forgot` | The password reset page. |
|
||
| `inactivity` | `/logout-inactivity` | The page to redirect to after inactivity. |
|
||
| `login` | `/login` | The login page. |
|
||
| `logout` | `/logout` | The logout page. |
|
||
| `reset` | `/reset` | The password reset page. |
|
||
| `unauthorized` | `/unauthorized` | The unauthorized page. |
|
||
|
||
<Banner type="success">
|
||
<strong>Note:</strong>
|
||
You can also swap out entire _views_ out for your own, using the `admin.views` property of the Payload Config. See [Custom Views](./views) for more information.
|
||
</Banner>
|
||
|
||
## I18n
|
||
|
||
The Payload Admin Panel is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/beta/packages/translations). Languages are automatically detected based on the user's browser and used by the Admin Panel to display all text in that language. If no language was detected, or if the user's language is not yet supported, English will be chosen. Users can easily specify their language by selecting one from their account page. See [I18n](../configuration/i18n) for more information.
|
||
|
||
## Light and Dark Modes
|
||
|
||
Users in the Admin Panel have the ability to choose between light mode and dark mode for their editing experience. Users can select their preferred theme from their account page. Once selected, it is saved to their user's preferences and persisted across sessions and devices. If no theme was selected, the Admin Panel will automatically detect the operation system's theme and use that as the default.
|