Statistic
Highlight a metric with title, value, units, loading, error, icon, and plain or card surfaces.
Statistic renders a single KPI — title, primary figure, optional units, decorative icon, and loading, empty, or error treatment. Pair metrics in Statistic Group for list exclusive areas and detail header anchors. Placement norms live on List Page Statistics.
When To Use
- Highlight 3–4 key metrics above a resource list (Ant exclusive area) or beside a detail header.
- Prefer
variant="card"in liststatsslots on muted page bodies; usevariant="plain"in detailPageHeader.extraContent. - Show per-metric loading while KPI queries run; use empty values for “no figure yet,” not a blank strip.
- Use
errorwhen the summary query failed — not when the filtered set is empty. - Reach for charts or a dedicated analytics page when you need trends, sparklines, or more than about six KPIs.
- Do not put Create / Export / column tools in the stats region — those stay PageHeader / toolbar.
- Do not use Statistic for derived amounts inside a fill form (system total, final payable between Form fields). Those stay form-density readouts via Formily
FormItem— see Form Layout — Derived Values Inside Forms.
Data Loading
Filter-scoped KPIs need a sibling summary API (or list aggregates) and a parallel TanStack Query — not a sum of the current table page. See List Page Statistics — Backend Contract and Frontend Data Loading.
Features
| Area | Behavior |
|---|---|
| Title | Optional label above the value (text-muted-foreground) |
| Value | Number or string; numbers get tabular-nums, optional precision and separators |
| Units | prefix / suffix for currency, icons, or unit labels |
| Icon | Decorative trailing node; root becomes a space-between row (metric stack | icon) |
| Loading | Skeleton on the value region; title stays visible |
| Empty | null / empty → Empty Value Placeholder em dash |
| Error | Destructive error copy in the value region (errorText, default "Unavailable") |
| Variants | plain (default) or card elevated surface |
| Group | Statistic Group responsive grid (about 1 → 4 columns) |
| Lifecycle tone | Optional value tint via classNames.value — align with StatusTag tones for pipeline KPIs (see Pipeline KPIs) |
Installing
pnpm dlx shadcn@latest add https://ui.isaacfei.com/r/statistic.jsonnpx shadcn@latest add https://ui.isaacfei.com/r/statistic.jsonyarn dlx shadcn@latest add https://ui.isaacfei.com/r/statistic.jsonbun x shadcn@latest add https://ui.isaacfei.com/r/statistic.jsonWith a namespace: npx shadcn@latest add @f-ui/statistic.
registryDependencies: shadcn skeleton, Empty Value Placeholder.
Usage
import { Statistic } from "@/components/f-ui/statistic/statistic";
import { StatisticGroup } from "@/components/f-ui/statistic/statistic-group";
<StatisticGroup>
<Statistic title="Active users" value={112893} />
<Statistic title="Balance" value={112893} precision={2} prefix="¥" />
</StatisticGroup>Examples
Basic
Plain metrics with thousand grouping and optional decimal precision.
"use client";
import { Statistic } from "@/components/f-ui/statistic/statistic";
export function StatisticDemo() {
return (
<div className="flex flex-wrap gap-8">
<Statistic title="Active users" value={112893} />
<Statistic title="Account balance (CNY)" value={112893.0} precision={2} />
</div>
);
}Card Variant
Elevated cards inside Statistic Group — the usual list exclusive-area look on a muted page body.
"use client";
import { Statistic } from "@/components/f-ui/statistic/statistic";
import { StatisticGroup } from "@/components/f-ui/statistic/statistic-group";
export function StatisticCardDemo() {
return (
<StatisticGroup>
<Statistic variant="card" title="Open tickets" value={128} />
<Statistic variant="card" title="Resolved today" value={47} />
<Statistic
variant="card"
title="Avg. handle time"
value={12.5}
precision={1}
suffix="min"
/>
<Statistic
variant="card"
title="Satisfaction"
value={98.2}
precision={1}
suffix="%"
/>
</StatisticGroup>
);
}Loading And Empty
loading keeps the title and skeletons the value; value={null} shows the empty placeholder.
"use client";
import { Statistic } from "@/components/f-ui/statistic/statistic";
export function StatisticLoadingDemo() {
return (
<div className="flex flex-wrap gap-8">
<Statistic title="Active users" value={112893} loading />
<Statistic title="Pending approvals" value={null} />
</div>
);
}Icon And Error
Trailing decorative icons sit beside the metric stack. Failed summary queries use error (destructive copy) instead of inventing a zero.
"use client";
import { FileIcon } from "lucide-react";
import { Statistic } from "@/components/f-ui/statistic/statistic";
import { StatisticGroup } from "@/components/f-ui/statistic/statistic-group";
export function StatisticErrorIconDemo() {
return (
<StatisticGroup>
<Statistic
title="Total files"
value={128}
variant="card"
icon={<FileIcon aria-hidden className="size-5" strokeWidth={1} />}
/>
<Statistic title="Total files" value={undefined} error variant="card" />
</StatisticGroup>
);
}Empty ≠ Error
value= (or other empty values) means a successful empty figure — the em dash. error means the fetch failed and needs recovery at the host; do not collapse query failures into empty.
Statistic Group
Responsive grid of plain metrics without card chrome — useful in detail extraContent or tight headers.
"use client";
import { Statistic } from "@/components/f-ui/statistic/statistic";
import { StatisticGroup } from "@/components/f-ui/statistic/statistic-group";
export function StatisticGroupDemo() {
return (
<StatisticGroup>
<Statistic title="Total orders" value={2840} />
<Statistic title="Revenue" prefix="$" value={482910} />
<Statistic title="Refund rate" value={1.8} precision={1} suffix="%" />
<Statistic title="New customers" value={316} />
</StatisticGroup>
);
}Pipeline KPIs
Document indexing and other lifecycle lists: keep Total neutral, tint Indexed / Processing / Failed to match StatusTag tones in the table. Use loading on in-flight counts; only apply destructive color when failed > 0.
"use client";
import { Statistic } from "@/components/f-ui/statistic/statistic";
import { StatisticGroup } from "@/components/f-ui/statistic/statistic-group";
/** Mock pipeline summary — same fields a `/documents/summary` endpoint would return. */
const PIPELINE = {
total: 2840,
indexed: 2712,
processing: 89,
failed: 39,
};
export function StatisticPipelineDemo() {
const { total, indexed, processing, failed } = PIPELINE;
return (
<StatisticGroup>
<Statistic variant="card" title="Total" value={total} />
<Statistic
variant="card"
title="Indexed"
value={indexed}
classNames={{ value: "text-success" }}
/>
<Statistic
variant="card"
title="Processing"
value={processing}
classNames={{ value: "text-info" }}
/>
<Statistic
variant="card"
title="Failed"
value={failed}
classNames={{
value: failed > 0 ? "text-destructive" : undefined,
}}
/>
</StatisticGroup>
);
}Norms and backend shape: List Page Statistics — Pipeline and Lifecycle KPIs.
Composition
StatisticGroup
└── Statistic (×n)
├── metric (min-w-0)
│ ├── header / title
│ └── content
│ ├── Skeleton (loading)
│ ├── error text (error)
│ ├── EmptyValuePlaceholder (empty)
│ └── prefix? / value / suffix?
└── icon?For list page order (filter → statistics → list → batch) and filter-scoped vs global KPIs, see List Page Statistics.
API Reference
Statistic Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | — | Metric label above the value. |
value | number | string | null | — | Primary figure; empty values render the empty placeholder. |
prefix | ReactNode | — | Content before the value (currency, icon). |
suffix | ReactNode | — | Content after the value (unit label). |
precision | number | — | Decimal places when value is a number. |
groupSeparator | string | "," | Thousands separator for numeric values. |
decimalSeparator | string | "." | Decimal separator for numeric values. |
loading | boolean | false | Skeleton on the value region; title stays visible. Precedence over error. |
error | boolean | false | Show destructive error copy instead of the value (when not loading). |
errorText | ReactNode | "Unavailable" | Error copy shown when error is true. |
icon | ReactNode | — | Decorative trailing icon; distinct from prefix units beside the value. |
variant | "plain" | "card" | "plain" | Surface style; prefer card on list stats slots. |
className | string | — | Merged onto the root. |
classNames | Partial<Record<StatisticSlot, string>> | — | Per-slot classes. |
Statistic Slots
| Slot | Applied to |
|---|---|
root | Outer wrapper |
header | Title row |
title | Title text |
content | Value row (prefix / value / suffix) |
value | Formatted figure or error text |
prefix | Leading unit / icon |
suffix | Trailing unit |
icon | Decorative trailing icon |
Statistic Group Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Prefer Statistic children. |
columns | number | responsive | Fixed column count when set; otherwise ~1 → 4 by breakpoint. |
className | string | — | Group root classes. |