f-ui
Components

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 list stats slots on muted page bodies; use variant="plain" in detail PageHeader.extraContent.
  • Show per-metric loading while KPI queries run; use empty values for “no figure yet,” not a blank strip.
  • Use error when 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

AreaBehavior
TitleOptional label above the value (text-muted-foreground)
ValueNumber or string; numbers get tabular-nums, optional precision and separators
Unitsprefix / suffix for currency, icons, or unit labels
IconDecorative trailing node; root becomes a space-between row (metric stack | icon)
LoadingSkeleton on the value region; title stays visible
Emptynull / empty → Empty Value Placeholder em dash
ErrorDestructive error copy in the value region (errorText, default "Unavailable")
Variantsplain (default) or card elevated surface
GroupStatistic Group responsive grid (about 1 → 4 columns)
Lifecycle toneOptional 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.json
npx shadcn@latest add https://ui.isaacfei.com/r/statistic.json
yarn dlx shadcn@latest add https://ui.isaacfei.com/r/statistic.json
bun x shadcn@latest add https://ui.isaacfei.com/r/statistic.json

With 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.

Active users
112,893
Account balance (CNY)
112,893.00
"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.

Open tickets
128
Resolved today
47
Avg. handle time
12.5min
Satisfaction
98.2%
"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.

Active users
Pending approvals
"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.

Total files
128
Total files
Unavailable
"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.

Total orders
2,840
Revenue
$482,910
Refund rate
1.8%
New customers
316
"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.

Total
2,840
Indexed
2,712
Processing
89
Failed
39
"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

PropTypeDefaultDescription
titleReactNodeMetric label above the value.
valuenumber | string | nullPrimary figure; empty values render the empty placeholder.
prefixReactNodeContent before the value (currency, icon).
suffixReactNodeContent after the value (unit label).
precisionnumberDecimal places when value is a number.
groupSeparatorstring","Thousands separator for numeric values.
decimalSeparatorstring"."Decimal separator for numeric values.
loadingbooleanfalseSkeleton on the value region; title stays visible. Precedence over error.
errorbooleanfalseShow destructive error copy instead of the value (when not loading).
errorTextReactNode"Unavailable"Error copy shown when error is true.
iconReactNodeDecorative trailing icon; distinct from prefix units beside the value.
variant"plain" | "card""plain"Surface style; prefer card on list stats slots.
classNamestringMerged onto the root.
classNamesPartial<Record<StatisticSlot, string>>Per-slot classes.

Statistic Slots

SlotApplied to
rootOuter wrapper
headerTitle row
titleTitle text
contentValue row (prefix / value / suffix)
valueFormatted figure or error text
prefixLeading unit / icon
suffixTrailing unit
iconDecorative trailing icon

Statistic Group Props

PropTypeDefaultDescription
childrenReactNodePrefer Statistic children.
columnsnumberresponsiveFixed column count when set; otherwise ~1 → 4 by breakpoint.
classNamestringGroup root classes.

On this page