Widgets
A widget is a card on the Cockpit dashboard. There are exactly two renderers, selected by component:
| Component | Renders |
|---|---|
EntityCount | A numeric KPI. |
EntityList | The top N rows. |
An unknown component value renders a placeholder, so a typo shows up as a visible wrong card rather than as a blank dashboard.
Both renderers
"widgets": [
{
"id": "suppliers-total",
"component": "EntityCount",
"title": { "en": "Suppliers", "de": "Lieferanten" },
"size": "small",
"position": 10,
"refresh_interval": 300,
"entity": "suppliers",
"filters": { "status": "active" },
"link_to": "/suppliers"
},
{
"id": "suppliers-recent",
"component": "EntityList",
"title": { "en": "Recently added" },
"size": "large",
"position": 20,
"entity": "suppliers",
"limit": 5,
"sort": { "column": "created_at", "direction": "desc" },
"columns": [
{ "name": "code", "label": "Code", "type": "text" },
{ "name": "name", "label": { "en": "Name" }, "type": "text" },
{ "name": "status", "label": "Status", "type": "badge", "vocabulary": "suppliers.statuses" }
],
"link_to": "/suppliers"
}
]
Every key
| Field | Applies to | Meaning |
|---|---|---|
id | Both | Required. Lowercase, hyphenated. |
component | Both | EntityCount or EntityList. |
title | Both | The card heading. Localisable. |
size | Both | small, medium (default), large or full. |
position | Both | Sort order across all installed apps' widgets. |
refresh_interval | Both | Auto-refresh in seconds. Minimum 5. |
entity | Both | The entity the widget reads, as a bare name. |
filters | Both | Filters merged into the query. Values may be bare (equality shorthand) or operator-prefixed, such as ilike.*foo*. |
limit | EntityList | Rows to fetch. Default 5. |
sort | EntityList | { column, direction }. |
columns | EntityList | The column projection. Same shape as a list view's columns, including the 26 types. |
link_to | Both | The Cockpit route the header and "view all" navigate to. Same studio-relative path semantics as a navigation entry. |
Declare sort on anything called "recent"
sort, an EntityList shows whatever the database returned first — which is physical row order, not time order. A "latest suppliers" card titled by time and ordered by nothing is a card that quietly lies.This is the same rule as on a list view, and it bites harder here because a dashboard card carries no visible sort control to correct it.
Guidance
Two or three widgets, not ten. The dashboard is shared across every installed app, exactly like the sidebar. position orders your cards among everyone else's.
Point link_to somewhere useful. A count nobody can drill into is a number without a next step.
Use filters to make the number mean something. "Suppliers" is a count of rows; "Active suppliers" is a fact an operator can act on.
Set refresh_interval deliberately. Every card that refreshes is a query per interval per open dashboard. 300 seconds is fine for a count; 5 is almost never justified.
Give a badge column a vocabulary. A hard-coded status map cannot show a value the merchant added after you shipped.
Where to go next
- List views — the column shapes
EntityListreuses, and KPIs, which are the in-page equivalent ofEntityCount. - Navigation — the studio-relative routes
link_tofollows. - Analytics Studio — where reporting and charts live.