---
tags: [payloadcms, tech-patterns]
topic: payloadcms
sources: [raw/admin__overview.md, raw/admin__preferences.md, raw/admin__preview.md, raw/admin__react-hooks.md]
created: 2026-05-15
---

# PayloadCMS — Admin Panel

## Overview

- Payload auto-generates a full CRUD admin panel built on **Next.js App Router** with RSC support
- Panel lives at `app/(payload)/admin/[[...segments]]/page.tsx` — all routes are Next.js routes
- REST (`/api`) and GraphQL (`/graphql`) APIs live side-by-side in the same route group
- Fully white-label capable: custom CSS, custom React components, custom routes
- 30+ language i18n, light/dark mode, per-user preferences persisted across sessions

## Config / Setup

```ts
import { buildConfig } from 'payload'

const config = buildConfig({
  admin: {
    user: 'admins',                  // auth-enabled collection slug
    dateFormat: 'dd/MM/yyyy',
    theme: 'dark',                   // 'light' | 'dark' | 'all'
    meta: { titleSuffix: '— MyApp' },
    livePreview: { url: '...' },
    timezones: {
      defaultTimezone: 'Europe/Amsterdam',
      supportedTimezones: ({ defaultTimezones }) => [
        ...defaultTimezones,
        { label: 'UTC', value: 'UTC' },
      ],
    },
    toast: { duration: 6000, position: 'bottom-right', limit: 3 },
    routes: {
      account: '/my-account',
      login: '/login',
    },
  },
  routes: {
    admin: '/dashboard',  // move panel off /admin
    api: '/api',
  },
})
```

## Key Options

| Option | Default | Notes |
|--------|---------|-------|
| `admin.user` | `'users'` | Slug of auth-enabled collection |
| `admin.theme` | `'all'` | Restrict to light / dark |
| `admin.dateFormat` | ISO | Any `date-fns` pattern |
| `admin.timezones` | — | DB always stores UTC; display only |
| `admin.toast.duration` | `4000` ms | Uses Sonner library |
| `routes.admin` | `/admin` | Moving requires updating Next.js dir structure |

### Admin-level routes (behind `/admin`)

`account`, `createFirstUser`, `forgot`, `inactivity`, `login`, `logout`, `reset`, `unauthorized`

## Preview

Preview generates a link from the admin edit view to your front-end. Different from Live Preview (iframe).

```ts
// Collection config
admin: {
  preview: (doc, { req, token }) =>
    doc?.enabled ? `${req.protocol}//${req.host}/${doc.slug}` : null,
}
```

### Draft Preview (Next.js)

1. Preview URL → `/preview?slug=...&previewSecret=...&path=...`
2. Route handler at `/app/preview/route.ts` verifies secret, calls `payload.auth()`, calls `draftMode().enable()`, redirects to `path`
3. Page component reads `(await draftMode()).isEnabled` and passes `draft: true` to `payload.find()`

## Preferences API

Per-user persistent preferences stored in `payload-preferences` collection.

```tsx
'use client'
import { usePreferences } from '@payloadcms/ui'

export function ColorPicker() {
  const { getPreference, setPreference } = usePreferences()

  useEffect(() => {
    getPreference<string[]>('last-used-colors').then(setColors)
  }, [getPreference])

  const addColor = (color: string) =>
    setPreference('last-used-colors', [...colors, color])
}
```

`getPreference(key)` → `Promise<T>` | `setPreference(key, value)` → `void`

## React Hooks (client components only)

All hooks from `@payloadcms/ui`. Require `'use client'` directive.

### Form hooks

| Hook | Purpose |
|------|---------|
| `useField({ path })` | Read/write a single field's value + state |
| `useFormFields(selector)` | Select specific fields — minimal re-renders |
| `useAllFormFields()` | Full form state + `dispatchFields` |
| `useForm()` | Form methods: `submit`, `validateForm`, `getData`, `reset`, `addFieldRow` etc. |
| `useDocumentForm()` | Top-level form — useful inside Lexical block child forms |

```tsx
'use client'
import type { TextFieldClientComponent } from 'payload'
import { useField } from '@payloadcms/ui'

export const CustomTextField: TextFieldClientComponent = ({ path }) => {
  const { value, setValue } = useField<string>({ path })
  return <input value={value ?? ''} onChange={e => setValue(e.target.value)} />
}
```

**Lexical gotcha:** `setValue` updates form data but does NOT re-render the editor UI. Use `dispatchFields` with both `value` and `initialValue`:

```tsx
dispatchFields({ type: 'UPDATE', path: 'body', value: newVal, initialValue: newVal })
```

### Document / collection hooks

| Hook | Returns |
|------|---------|
| `useDocumentInfo()` | `id`, `collectionSlug`, `globalSlug`, `docPermissions`, `isLocked`, `data`, `uploadStatus`, … |
| `useDocumentTitle()` | `title`, `setDocumentTitle` |
| `useListQuery()` | `data`, `query`, `handlePageChange`, `handleWhereChange`, … |
| `useSelection()` | `count`, `selected`, `toggleAll`, `totalDocs` |

### Auth / config hooks

| Hook | Returns |
|------|---------|
| `useAuth<User>()` | `user`, `logOut`, `token`, `refreshPermissions`, `permissions` |
| `useConfig()` | `config`, `getEntityConfig({ collectionSlug })` |
| `useLocale()` | `{ code, label, rtl }` |
| `useTheme()` | `theme`, `setTheme`, `autoMode` |
| `usePreferences()` | `getPreference`, `setPreference` |

### Misc hooks

- `useCollapsible()` — `isCollapsed`, `toggle`
- `useEditDepth()` — nesting depth in modals
- `useStepNav()` — breadcrumb control: `setStepNav(items)`
- `useDocumentEvents()` — cross-document update events
- `usePayloadAPI(url, options)` — reactive REST fetching: `[{ data, isLoading, isError }, { setParams }]`
- `useTableColumns()` — `setActiveColumns`, `toggleColumn`, `resetColumnsState`
- `useRouteTransition()` — `startRouteTransition` for programmatic navigation

## Gotchas

- `importMap.js` is auto-regenerated on startup/HMR — never edit it manually; `layout.tsx` is safe to edit
- Moving the admin panel (`routes.admin`) requires restructuring the Next.js `app/` folder to match
- Timezones are display-only; DB always stores UTC — must also enable `timezone: true` on each date field
- Multiple auth collections possible but only one can access the admin panel (`admin.user`)
- Client Components cannot receive non-serializable props from Server Components

## Related

- [[wiki/payloadcms/custom-components|Custom Components]]
- [[wiki/payloadcms/admin-panel-overview|Admin Panel Overview]]
- [[wiki/payloadcms/configuration|Configuration]]
