obsidian/wiki/payloadcms/dashboard-widgets.md
2026-05-15 15:13:56 +01:00

6.1 KiB

title aliases tags sources created updated
Dashboard Widgets
payload-dashboard
modular-dashboard
admin-dashboard-widgets
payloadcms
admin
custom-components
react
experimental
raw/custom-components__dashboard.md
2026-05-15 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:

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:

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:

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:

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