---
title: "Custom Views — Admin Panel"
aliases: [payload-custom-views, admin-custom-views, custom-admin-pages]
tags: [payloadcms, admin, custom-components, react, views]
sources: [raw/custom-components__custom-views.md]
created: 2026-05-15
updated: 2026-05-15
---

## Overview

Custom Views are React components rendered at the page level inside the Payload Admin Panel. They can either **replace built-in views** or **add entirely new pages** under `/admin`.

Four view scopes:
- **Root Views** — under `/admin` (dashboard, account, custom pages)
- **Collection Views** — under `/admin/collections/:slug` (list, edit)
- **Global Views** — under `/admin/globals/:slug` (edit)
- **Document Views** — nested inside Edit Views (see [[wiki/payloadcms/custom-components|Custom Components]])

---

## Replacing Built-in Views

Use `admin.components.views` in the appropriate config scope:

```ts
// Root-level (payload.config.ts)
admin: {
  components: {
    views: {
      dashboard: { Component: '/path/to/MyDashboard' },
      account:   { Component: '/path/to/MyAccount' },
    },
  },
},

// Collection-level
admin: {
  components: {
    views: {
      list: { Component: '/path/to/MyListView' },
      edit: { default: { Component: '/path/to/MyEditView' } },
    },
  },
},
```

View config properties:

| Property    | Required | Description |
|-------------|----------|-------------|
| `Component` | ✓        | Path to the React component (supports `#ExportName` for named exports) |
| `path`      | ✓ (new)  | URL path — any `path-to-regexp`-compatible string, must start with `/` |
| `exact`     |          | Match path exactly (no prefix matching) |
| `strict`    |          | Trailing slash must match |
| `sensitive` |          | Case-sensitive path matching |
| `meta`      |          | Page metadata overrides for the Admin Panel |

---

## Adding New Views

Add any custom key to the `views` object. Requires `Component` + `path`:

```ts
admin: {
  components: {
    views: {
      myCustomPage: {
        Component: '/path/to/MyPage#MyPageComponent',
        path: '/my-custom-page',
      },
    },
  },
},
```

> **Route cascading:** Routes match URLs that *start with* the path unless `exact: true`. Define nested routes before parent routes to avoid conflicts.

---

## Building Custom Views

Custom Views are standard [[wiki/payloadcms/custom-components|Custom Components]] rendered at page level.

### Props (Root Views)

| Prop             | Description |
|------------------|-------------|
| `initPageResult` | `{ req, payload, permissions, locale, visibleEntities }` |
| `clientConfig`   | Client-side Payload config |
| `importMap`      | Import map object |
| `params`         | Next.js dynamic route params |
| `searchParams`   | URL search params |
| `doc`            | Current document (Document Views only) |
| `i18n`           | i18n object — see [[wiki/payloadcms/i18n|I18n]] |
| `payload`        | Payload instance — see [[wiki/payloadcms/local-api|Local API]] |

> Collection List and Edit Views receive different props — see List View and Document Views docs.

### Minimal Custom View

```tsx
import type { AdminViewServerProps } from 'payload'
import { Gutter } from '@payloadcms/ui'

export function MyCustomView(props: AdminViewServerProps) {
  return (
    <Gutter>
      <h1>My Custom Page</h1>
    </Gutter>
  )
}
```

### With Default Template (nav + layout)

```tsx
import type { AdminViewServerProps } from 'payload'
import { DefaultTemplate } from '@payloadcms/next/templates'
import { Gutter } from '@payloadcms/ui'

export function MyCustomView({ initPageResult, params, searchParams }: AdminViewServerProps) {
  return (
    <DefaultTemplate
      i18n={initPageResult.req.i18n}
      locale={initPageResult.locale}
      params={params}
      payload={initPageResult.req.payload}
      permissions={initPageResult.permissions}
      searchParams={searchParams}
      user={initPageResult.req.user || undefined}
      visibleEntities={initPageResult.visibleEntities}
    >
      <Gutter>
        <h1>My Page</h1>
      </Gutter>
    </DefaultTemplate>
  )
}
```

---

## Securing Custom Views

**Custom Views are public by default.** You must implement auth checks manually:

```tsx
export function MyCustomView({ initPageResult }: AdminViewServerProps) {
  const { req: { user } } = initPageResult

  if (!user) {
    return <p>You must be logged in to view this page.</p>
  }

  return <Gutter><h1>Protected Page</h1></Gutter>
}
```

---

## Root View Options

| Key         | Description |
|-------------|-------------|
| `dashboard` | Main Admin Panel landing page (also supports modular widgets) |
| `account`   | Logged-in user account page |
| `[key]`     | Any custom key adds a new root-level page |

## Collection View Options

| Key    | Description |
|--------|-------------|
| `list` | Collection document list |
| `edit` | Document edit view (nested Document Views) |
| `[key]`| New collection-scoped page |

## Global View Options

| Key    | Description |
|--------|-------------|
| `edit` | Global document edit view (nested Document Views) |
| `[key]`| New global-scoped page |

---

## Key Takeaways

- `admin.components.views` is the single config point — applies at root, collection, and global scope
- New views need `Component` + `path`; replacement views only need `Component`
- **All custom views are public** — implement `req.user` checks yourself
- Use `DefaultTemplate` from `@payloadcms/next/templates` for consistent nav/layout
- Routes cascade by default — use `exact: true` or order routes carefully
- Named exports: `'/path/to/Component#ExportName'` syntax
- Dashboard is a special case: supports both full replacement and modular widgets

---

## Sources

- `raw/custom-components__custom-views.md`
- Official docs: https://payloadcms.com/docs/custom-components/custom-views
