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:
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:
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:
{
"@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:
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
widgets: [
{
id: "stats",
component: StatsWidget,
settingsComponent: StatsSettings,
},
]Sizing and Grid Responsiveness
Columns collapse responsively across screen viewports:
| Viewport | Columns | Behavior |
|---|---|---|
< 768px (Mobile) | 1 | All widgets occupy full width |
768px – 1279px (Tablet) | 2 | Max 2 columns; span 3 collapses to 2 |
≥ 1280px (Desktop) | 3 | Renders full declared span (1, 2, or 3) |
Widget Configuration Options
Prop
Type