---
title: "Dashboard Widgets"
aliases: [payload-dashboard, modular-dashboard, admin-dashboard-widgets]
tags: [payloadcms, admin, custom-components, react, experimental]
sources: [raw/custom-components__dashboard.md]
created: 2026-05-15
updated: 2026-05-15
---

# Dashboard Widgets

> **Experimental feature** — the Modular Dashboard may change in future releases.

The Dashboard is the first screen after login. By default it shows collection/global cards. Custom **widgets** replace or extend this with any React Server Component content.

## Defining Widgets

Add widgets in `admin.dashboard.widgets` inside `buildConfig`:

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

export default buildConfig({
  admin: {
    dashboard: {
      widgets: [
        {
          slug: 'sales-summary',
          Component: './components/SalesSummary.tsx#default',
          fields: [
            { name: 'title', type: 'text' },
            { name: 'timeframe', type: 'select', options: ['daily', 'weekly', 'monthly', 'yearly'] },
            { name: 'showTrend', type: 'checkbox' },
          ],
          minWidth: 'small',
          maxWidth: 'medium',
        },
      ],
    },
  },
})
```

## Widget Config Properties

| Property    | Type          | Description                                                       |
|-------------|---------------|-------------------------------------------------------------------|
| `slug` *    | `string`      | Unique identifier                                                 |
| `Component` * | `string`    | File path; `#name` for named exports                              |
| `fields`    | `Field[]`     | Optional form fields shown in the widget edit drawer              |
| `minWidth`  | `WidgetWidth` | Minimum resize width (default: `'x-small'`)                       |
| `maxWidth`  | `WidgetWidth` | Maximum resize width (default: `'full'`)                          |

**WidgetWidth values:** `'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'full'`

## Creating a Widget Component

Widgets are **React Server Components** receiving `WidgetServerProps`:

```tsx
import type { WidgetServerProps } from 'payload'

export default async function UserStatsWidget({ req }: WidgetServerProps) {
  const userCount = await req.payload.count({ collection: 'users' })

  return (
    <div className="card">
      <h3>Total Users</h3>
      <p style={{ fontSize: '32px', fontWeight: 'bold' }}>{userCount.totalDocs}</p>
    </div>
  )
}
```

**UI consistency tips:**
- Use `className="card"` on the root element for the panel background
- Use CSS variables: `var(--theme-elevation-0)` for bg, `var(--theme-text)` for text

### Typed Widget Data

When `fields` are defined, type `widgetData` using generated types:

```tsx
import type { WidgetServerProps } from 'payload'
import type { SalesSummaryWidget } from '../payload-types'

export default async function SalesSummaryWidgetComponent({
  widgetData,
}: WidgetServerProps<SalesSummaryWidget>) {
  const title = widgetData?.title ?? 'Sales Summary'
  const timeframe = widgetData?.timeframe ?? 'monthly'

  return <div className="card"><h3>{title} ({timeframe})</h3></div>
}
```

## Default Layout

Control the initial dashboard layout with `defaultLayout` — a function receiving `req`:

```ts
dashboard: {
  defaultLayout: ({ req }) => {
    const isAdmin = req.user?.roles?.includes('admin')
    return [
      { widgetSlug: 'collections', width: 'full' },
      { widgetSlug: 'sales-summary', data: { timeframe: 'monthly', title: 'Revenue Overview' }, width: isAdmin ? 'medium' : 'small' },
      { widgetSlug: 'user-stats', width: isAdmin ? 'medium' : 'full' },
    ]
  },
  widgets: [ /* ... */ ],
}
```

### WidgetInstance Type

| Property      | Type          | Description                                          |
|---------------|---------------|------------------------------------------------------|
| `widgetSlug` * | `string`     | Slug of the widget to render                         |
| `data`        | `object`      | Initial values passed as `widgetData` to the component |
| `width`       | `WidgetWidth` | Initial width (clamped by `minWidth`/`maxWidth`)     |

> `defaultLayout` is only applied for first-time visitors or after a layout reset. Returning users see their saved layout.

## Built-in Widgets

- `collections` — the default collection/global cards widget
- Automatically added if no `defaultLayout` is defined

## User Customization Flow

Users can customize their own dashboard via the breadcrumb dropdown → **Edit Dashboard**:

1. Add widgets via "Add +"
2. Edit widget field data via the edit button
3. Resize via the width dropdown (if multiple widths allowed)
4. Reorder via drag-and-drop
5. Delete widgets
6. Save or cancel changes
7. Reset to `defaultLayout` via "Reset Layout"

Layouts are persisted to user preferences in the database.

## Plugin Widgets

Each plugin can register its own widgets. Common plugin use cases:
- Analytics dashboards
- Error reporting (Sentry)
- Document count filters
- Recent jobs executed

## Key Takeaways

- **Experimental** — API may change; `admin.dashboard.widgets` array in `buildConfig`
- Widget components are **RSC** — fetch data server-side via `req.payload.*`
- `fields` array adds a config drawer for per-instance data (typed via generated `payload-types`)
- `defaultLayout` function receives `req` — use it to customize layout per user role
- Users can fully customize and save their own layout; `defaultLayout` = first-visit default only
- Built-in `collections` widget is auto-added if no `defaultLayout` is set
- Use `className="card"` + CSS theme variables for visual consistency with Payload UI
- Plugins can contribute their own widgets

## Related

- [[wiki/payloadcms/custom-components|Custom Components]] — root slots, edit/list view slots, providers
- [[wiki/payloadcms/react-hooks|React Hooks]] — `usePreferences`, `useAuth`, `useDocumentInfo`
- [[wiki/payloadcms/admin-preferences|Admin Preferences]] — per-user preference storage (where layouts are saved)
- [[wiki/payloadcms/plugins-api|Plugin API]] — how plugins can register their own widgets
- [[wiki/payloadcms/custom-views|Custom Views]] — full custom Admin Panel pages (vs widgets)
