PluginsAdmin

Dashboard Widgets

Add drag-and-drop widgets to the AdminCP dashboard from your plugin with customizable layouts, sizing, and settings dialogs.

The AdminCP dashboard at /admin/core is an interactive grid of widgets. Each administrator arranges their own board: drag to reorder, resize cards, or add widgets from the side panel.

Register a Widget

1. Write the Widget Component

Create a React component accepting AdminDashboardWidgetProps:

plugins/blog/src/views/admin/widgets/stats-widget.tsx
import type { AdminDashboardWidgetProps } from "@vitnode/core/lib/plugin"

export const StatsWidget = ({ settings }: AdminDashboardWidgetProps) => {
  return (
    <div className="flex flex-col gap-2 p-2">
      <span className="text-sm text-muted-foreground">Total Published Articles</span>
      <p className="text-3xl font-bold tabular-nums">42</p>
    </div>
  )
}

2. Register Widget in Plugin Config

Add the widget to admin.dashboard.widgets in src/config.tsx:

plugins/blog/src/config.tsx
import { buildPlugin } from "@vitnode/core/lib/plugin"
import { BarChart3Icon } from "lucide-react"
import { StatsWidget } from "./views/admin/widgets/stats-widget"

export const blogPlugin = () =>
  buildPlugin({
    pluginId: "blog",
    admin: {
      dashboard: {
        widgets: [
          {
            id: "stats",
            component: StatsWidget,
            icon: <BarChart3Icon />,
            defaultSpan: 1,
            defaultRows: 1,
            defaultEnabled: true,
          },
        ],
      },
    },
  })

3. Add Translations

Provide the widget's title and description in your plugin's locale file:

plugins/blog/src/locales/en.json
{
  "@vitnode/blog": {
    "admin": {
      "dashboard": {
        "widgets": {
          "stats": {
            "title": "Blog Statistics",
            "desc": "Overview of published articles and views."
          }
        }
      }
    }
  }
}

Adding a Settings Dialog

Admins can configure widget preferences (e.g. date range or filtering) via a gear icon on the card:

1. Create the Settings Form with AutoForm

Use useWidgetSettingsDialog to save values and close the dialog:

plugins/blog/src/views/admin/widgets/stats-settings.tsx
import { AutoForm } from "@vitnode/core/components/form/auto-form"
import { AutoFormSelect } from "@vitnode/core/components/form/fields/select"
import { useWidgetSettingsDialog } from "@vitnode/core/views/admin/views/core/dashboard/grid/widget-settings-dialog"
import { z } from "zod"

const formSchema = z.object({
  range: z.enum(["month", "year"]).default("month"),
})

export const StatsSettings = ({ settings }: { settings: { range?: "month" | "year" } }) => {
  const { save } = useWidgetSettingsDialog()

  return (
    <AutoForm
      fields={[
        {
          id: "range",
          component: (props) => <AutoFormSelect label="Date Range" {...props} />,
        },
      ]}
      formSchema={formSchema}
      onSubmit={async (values) => {
        await save({ range: values.range }) 
      }}
      submitButtonProps={{ children: "Save Settings" }}
    />
  )
}

2. Attach settingsComponent in Config

plugins/blog/src/config.tsx
widgets: [
  {
    id: "stats",
    component: StatsWidget,
    settingsComponent: StatsSettings, 
  },
]

Sizing and Grid Responsiveness

Columns collapse responsively across screen viewports:

ViewportColumnsBehavior
< 768px (Mobile)1All widgets occupy full width
768px – 1279px (Tablet)2Max 2 columns; span 3 collapses to 2
≥ 1280px (Desktop)3Renders full declared span (1, 2, or 3)

Widget Configuration Options

Prop

Type

Learn More