Navigation: WP Admin > NimblyMail > Dashboard
The dashboard renders an extension slot below the Recent Activity table. A Pro add-on injects widgets by filtering nimbly_mail_dashboard_widgets. Six built-in widget types cover the majority of use cases without any Vue code. A custom type is available when a bespoke component is required.
Widget Descriptor Reference
| Key | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique widget identifier. |
type |
string |
Yes | Widget type. See types below. |
label |
string |
Yes | Widget heading. |
icon |
string |
No | Ant Design icon name (e.g., WarningOutlined). |
position |
string |
No | Insertion hint: after-stats, after-activity (default), before-stats. |
data |
array |
No | Inline data object. Used when no endpoint is needed. |
data_endpoint |
string |
No | REST endpoint path the widget fetches on mount (relative to wp-json/). |
refresh_interval |
int |
No | Seconds between automatic re-fetches. Requires data_endpoint. |
renderer |
string |
No | Custom renderer slug. Required when type is custom. |
Use either data or data_endpoint — not both. If both are present, data_endpoint takes precedence.
Widget Types
stat-card
Renders a single metric with an optional trend indicator.
Expected data shape:
array(
'value' => 98.6, // numeric value
'unit' => '%', // string appended after the value
'trend' => 'up', // 'up' | 'down' | 'neutral' (optional)
)
Example:
add_filter(
'nimbly_mail_dashboard_widgets',
function ( array $widgets ): array {
$widgets[] = array(
'id' => 'bounce-rate',
'type' => 'stat-card',
'label' => __( 'Bounce Rate (24h)', 'nimbly-mail-pro' ),
'icon' => 'WarningOutlined',
'position' => 'after-activity',
'data_endpoint' => 'nimbly-mail-pro/v1/stats/bounce-rate',
'refresh_interval' => 300,
);
return $widgets;
}
);
@since 1.0.0 — nimbly_mail_dashboard_widgets filter.
Signature: apply_filters( 'nimbly_mail_dashboard_widgets', array $widgets, DashboardController $controller )
$widgets— the current widgets array.$controller— theDashboardControllerinstance, available for context (e.g., reading request parameters).
Returns: array
table
Renders a scrollable Ant Design table.
Expected data shape:
array(
'columns' => array(
array( 'key' => 'email', 'title' => 'Email Address' ),
array( 'key' => 'reason', 'title' => 'Bounce Reason' ),
array( 'key' => 'date', 'title' => 'Date' ),
),
'rows' => array(
array( 'email' => '[email protected]', 'reason' => 'Hard bounce', 'date' => '2025-04-20' ),
),
)
Example:
$widgets[] = array(
'id' => 'recent-bounces',
'type' => 'table',
'label' => __( 'Recent Bounces', 'nimbly-mail-pro' ),
'icon' => 'TableOutlined',
'position' => 'after-activity',
'data_endpoint' => 'nimbly-mail-pro/v1/bounces/recent',
);
activity-feed
Renders a chronological list of events with an icon, label, and timestamp per row.
Expected data shape:
array(
'items' => array(
array(
'icon' => 'CheckCircleOutlined',
'label' => 'Webhook received for [email protected]',
'timestamp' => '2025-04-20 14:32:01',
),
),
)
Example:
$widgets[] = array(
'id' => 'webhook-feed',
'type' => 'activity-feed',
'label' => __( 'Webhook Activity', 'nimbly-mail-pro' ),
'icon' => 'BellOutlined',
'position' => 'after-activity',
'data_endpoint' => 'nimbly-mail-pro/v1/webhooks/recent',
'refresh_interval' => 60,
);
alert-banner
Renders a dismissible Ant Design alert. Useful for licence warnings, quota notices, or connectivity errors.
Expected data shape:
array(
'type' => 'warning', // 'success' | 'info' | 'warning' | 'error'
'message' => 'Your Postmark sending quota is 80% used.',
'detail' => 'Upgrade your plan to avoid delivery interruptions.', // optional
)
Example:
$widgets[] = array(
'id' => 'quota-alert',
'type' => 'alert-banner',
'label' => __( 'Sending Quota', 'nimbly-mail-pro' ),
'position' => 'after-stats',
'data_endpoint' => 'nimbly-mail-pro/v1/quota/status',
);
status-list
Renders a list of named services with a status indicator (ok / warning / error) per row.
Expected data shape:
array(
'items' => array(
array( 'label' => 'Postmark API', 'status' => 'ok' ),
array( 'label' => 'Bounce Webhook', 'status' => 'warning' ),
array( 'label' => 'Smart Routing Rules', 'status' => 'ok' ),
),
)
Example:
$widgets[] = array(
'id' => 'pro-status',
'type' => 'status-list',
'label' => __( 'Pro Service Status', 'nimbly-mail-pro' ),
'icon' => 'CheckSquareOutlined',
'position' => 'after-stats',
'data_endpoint' => 'nimbly-mail-pro/v1/status',
'refresh_interval' => 120,
);
chart (stub)
The chart type is a reserved stub. The free plugin renders a placeholder; the first Pro widget that needs a chart is responsible for pulling its charting library in via its own enqueued asset. A full chart widget implementation is tracked as a follow-up.
custom
Opt out of generic rendering entirely. Set type to custom, provide a renderer slug, and register the Vue component from JavaScript.
PHP side:
$widgets[] = array(
'id' => 'smart-routing-map',
'type' => 'custom',
'label' => __( 'Routing Map', 'nimbly-mail-pro' ),
'position' => 'after-activity',
'renderer' => 'routing-map',
'data' => array( /* any shape your component expects */ ),
);
JavaScript side:
import RoutingMap from './components/RoutingMap.vue';
window.nimblyMail.registerDashboardWidget( 'routing-map', RoutingMap );
Network Dashboard
The same pattern applies to the network admin dashboard. Use nimbly_mail_network_dashboard_widgets instead.
Signature: apply_filters( 'nimbly_mail_network_dashboard_widgets', array $widgets, NetworkDashboardController $controller )
@since 1.0.0 — nimbly_mail_network_dashboard_widgets filter.
add_filter(
'nimbly_mail_network_dashboard_widgets',
function ( array $widgets ): array {
$widgets[] = array(
'id' => 'network-bounce-rate',
'type' => 'stat-card',
'label' => __( 'Network Bounce Rate (24h)', 'nimbly-mail-pro' ),
'icon' => 'GlobalOutlined',
'position' => 'after-activity',
'data_endpoint' => 'nimbly-mail-pro/v1/network/stats/bounce-rate',
);
return $widgets;
}
);
Polling
Widgets that declare both data_endpoint and refresh_interval are automatically re-fetched every refresh_interval seconds. Polling is paused while the browser tab is hidden (document.visibilityState === 'hidden') and resumes when the tab becomes visible again. This prevents unnecessary background requests when the user is not actively viewing the dashboard.
Setting refresh_interval without data_endpoint has no effect.
