Skip to main content

CnStatsBlock

Statistics display card with icon, count, and optional breakdown. Used inside CnKpiGrid.

Wraps: NcLoadingIcon

Try it​

Loading CnStatsBlock playground…

CnStatsBlock showing pipeline statistics

Props​

PropTypeDefaultDescription
titleString''Card title
countNumber0Main count value (formatted with toLocaleString)
countLabelString'objects'Unit label below count
breakdownObjectnullKey-value pairs for breakdown display
loadingBooleanfalseLoading state
loadingLabelString'Loading...'
emptyLabelString'No items found'
errorString | Boolean | ObjectnullThe tile could not load its number. Anything truthy counts — pass the caught error itself, or true. Shows a dash and errorLabel instead of a count, tints the tile error, and suppresses the breakdown. Takes precedence over count, loading and emptyLabel.
errorLabelString'Unavailable'Text shown in place of the count when error is set.
iconComponentnullMDI icon component
iconSizeNumber24Icon pixel size
variantString'default''default', 'primary', 'success', 'warning', 'error'
horizontalBooleanfalseDeprecated since 2.25.0 — icon-left is the canonical card's own layout, so this prop no longer changes anything. Kept for existing callers.
verticalBooleanfalseStack the icon above a centred number instead of placing it beside one.
filledBooleanfalseDraw the card's own grey box. Off by default: the block normally sits inside a wrapper that already draws a card, and a second box reads as a card inside a card.
clickableBooleanfalseEnable click interaction
showZeroCountBooleanfalseDisplay 0 as a count value instead of the empty label
routeObjectnullVue Router location object ({ name, path, query, ... }). When set, the card renders as a <router-link> and clickable styles are applied automatically.

Error state​

A tile that cannot load its number must not render one.

<CnStatsBlock
:title="t('myapp', 'Overdue')"
:count="count"
:loading="loading"
:error="error" />

error beats count, loading and emptyLabel, in that order of importance:

  • over count — a tile that fetched 42 and then failed to refresh must not keep presenting 42 as current.
  • over loading — a failed load is finished, not in progress.
  • over emptyLabel — "we could not read this" and "there is nothing here" mean opposite things to a reader, and used to look identical.

This exists because the alternative was observed in production: eleven tiles across five apps answered a failed fetch with catch { count = 0 }. A dashboard with a dead backend looked like a dashboard reporting genuinely empty collections, and zero is a number a reader believes.

The tile tints itself — do not also pass variant="error". Two props for one state means forgetting the second, and forgetting it is invisible: "Unavailable" in the default colour reads as ordinary content. An explicit variant is ignored while error is set.

An empty string is not an error: a caller clearing its message back to '' is reporting recovery.

Events​

EventPayloadDescription
clickeventBlock clicked (only if clickable)

Slots​

SlotBindingsDescription
#icon—Custom icon content
#valuecount (number), formatted (string)Override the prominently-displayed value with a pre-formatted string (currency, percent, a — placeholder, …). count stays the raw number — this is presentation only. Defaults to the localized count. When provided, the value area always renders (even at count 0).

Usage​

<CnStatsBlock
title="Active Contacts"
:count="150"
count-label="contacts"
variant="primary"
:breakdown="{ 'This week': 12, 'This month': 43 }"
:icon="AccountGroupOutline"
:clickable="true"
@click="navigateToContacts" />

Formatted value via the #value slot​

Keep count numeric and format the displayed value in the slot (currency, percent, a — placeholder):

<CnStatsBlock title="Total Pipeline Value" :count="totalValue" count-label="open opportunities">
<template #value>{{ formatCurrency(totalValue) }}</template>
</CnStatsBlock>

<CnStatsBlock title="Win Rate" :count="winRate ?? 0" count-label="closed deals">
<template #value>{{ winRate === null ? '—' : Math.round(winRate * 100) + '%' }}</template>
</CnStatsBlock>

Reference (auto-generated)​

The tables below are generated from the SFC source via vue-docgen-cli. They reflect what's actually in CnStatsBlock.vue and update automatically whenever the component changes.

Props​

NameTypeRequiredDefaultDescription
titlestring''Block title
countnumber0The main count number to display prominently
countLabelstring() =&gt; t('nextcloud-vue', 'objects')Label displayed next to the count
breakdownobjectnullDetailed breakdown object (key-value pairs)
loadingbooleanfalseWhether data is currently loading
loadingLabelstring() =&gt; t('nextcloud-vue', 'Loading...')Text shown while loading
emptyLabelstring() =&gt; t('nextcloud-vue', 'No items found')Text shown when count is 0
errorunionnullThe tile could not load its number. Anything truthy counts — pass the caught error itself, or just true. The tile then shows a dash and errorLabel INSTEAD of a count, and tints itself error. This takes precedence over count, loading and emptyLabel deliberately: a stale or defaulted number rendered during a failure is the exact thing this prop exists to stop. A dashboard showing 0 because the backend is down is worse than one showing nothing, because 0 is a number a reader will believe.
errorLabelstring() =&gt; t('nextcloud-vue', 'Unavailable')Text shown in place of the count when error is set.
iconobject&#124;funcnullIcon component (e.g., imported MDI icon)
iconSizenumber24Icon size in pixels
variantstring'default'Color variant: 'default', 'primary', 'success', 'warning', 'error'
verticalbooleanfalseStack the icon above a centred number instead of placing it beside one. The canonical KPI card is horizontal, so this is the opt-out; horizontal below is kept only for callers that already pass it.
horizontalbooleanfalseLay the icon left of the content. No longer needed — this is the canonical card's own layout — and kept so existing callers that pass horizontal keep working. Pass vertical to stack instead.
filledbooleanfalseDraw the card's own grey box. Off by default: a stats block is normally rendered inside a wrapper that already draws a card, and a second box reads as a card inside a card. Turn it on for a block mounted with no wrapper around it.
clickablebooleanfalseWhether the card is clickable
showZeroCountbooleanfalseWhether to display 0 as a count value instead of the empty label
routeobjectnullVue Router location object for declarative navigation. When set, the card renders as a <router-link> and clickable styles are implied. { name: 'Cases', query: { status: 'open' } } { path: '/catalogi' }

Events​

NamePayloadDescription
click—

Slots​

NameBindingsDescription
icon—
valuecount, formattedOverride the prominently-displayed value — render a pre-formatted string (currency, percent, a "—" placeholder, …). count stays the raw number; this is presentation only. Defaults to the localized count.