---
title: Stat
description: Display a labelled metric with an optional change indicator, plus a responsive group wrapper.
icon: chart-no-axes-column
mode: wide
---

<ComponentPreview name="stat-demo" />

## Installation

```package-install
npx shadcn@latest add @redpanda/stat
```

## Usage

`Stat` renders a single label and value pair. `StatGroup` arranges several `Stat`s into a responsive grid, ideal for KPI strips like a cluster overview. Stats in a group use CSS subgrid so labels, values, and sublabels row-align across the strip even when label lengths differ.

```tsx lineNumbers
import { Stat, StatGroup } from "@/components/redpanda-ui/stat"
```

```tsx lineNumbers
<StatGroup columns={4}>
  <Stat label="Brokers" value="3" mono />
  <Stat label="Topics" value="128" tone="muted" mono />
  <Stat label="Throughput" value="42 MB/s" size="lg" tone="success" mono />
  <Stat label="Under-replicated" value="4" tone="warning" mono />
</StatGroup>
```

## Anatomy

```
Stat Structure:
┌───────────────────────────────────┐
│ <StatGroup> (responsive grid)     │
│ ┌───────────────────────────────┐ │
│ │ <Stat>                        │ │
│ │  • label (uppercase caption)  │ │
│ │  • value (+ optional delta)   │ │
│ │  • sublabel (optional)        │ │
│ └───────────────────────────────┘ │
└───────────────────────────────────┘
```

## Variants

### Sizes

```tsx lineNumbers
<Stat label="Small" value="42" size="sm" />
<Stat label="Medium" value="42" size="md" />
<Stat label="Large" value="42" size="lg" />
```

### Tones

```tsx lineNumbers
<Stat label="Default" value="42" tone="default" />
<Stat label="Muted" value="42" tone="muted" />
<Stat label="Success" value="42" tone="success" />
<Stat label="Warning" value="42" tone="warning" />
<Stat label="Destructive" value="42" tone="destructive" />
```

### Monospaced

Use `mono` for tabular figures that should not jitter as values update.

```tsx lineNumbers
<Stat label="Throughput" value="42 MB/s" mono />
```

### Delta indicator

`delta` adds an optional change affordance. `direction` drives both the icon and the default color (`up` → success, `down` → destructive, `neutral` → muted). Override with `tone`.

```tsx lineNumbers
<Stat label="Throughput" value="42 MB/s" delta={{ value: "+12%", direction: "up" }} />
<Stat label="Consumer lag" value="312" delta={{ value: "-4.3%", direction: "down" }} />
<Stat label="Partitions" value="640" delta={{ value: "0%", direction: "neutral" }} />
```

<ComponentPreview name="stat-delta-demo" />

### Sublabel

`sublabel` hangs a muted secondary line below the value (e.g. a partition count under a topic count). It does not affect the value's baseline, so stats with and without a sublabel stay top-aligned inside a `StatGroup`.

```tsx lineNumbers
<Stat label="Topics" value="6" sublabel="6 partitions" size="lg" />
```

### Linked label

`labelLink` turns the label into a link. Pass a single link element with **no children** — the label text and a trailing arrow are injected, and the caption styling plus a hover affordance are merged onto it. Client-side routing stays with the caller, so any link component works (`<a>`, TanStack/Next `<Link>`, etc.).

```tsx lineNumbers
<Stat label="Topics" value="6" labelLink={<a href="/topics" />} />
<Stat label="Topics" value="6" labelLink={<Link to="/clusters/$clusterId/topics" params={{ clusterId }} />} />
```

### Group columns and gap

Each `columns` value is a ramp on the group's own width, not the viewport's, so a group in a card, a panel or a narrow column steps down instead of squeezing its values. `3` shows 3 columns from a 448px group and `4` shows 4 from 672px; `6` shows 3 from 672px and 6 from 896px. Below those, `3` shows 1 column and the others show 2.

```tsx lineNumbers
<StatGroup columns={2} gap="sm">{/* ... */}</StatGroup>
<StatGroup columns={3} gap="md">{/* ... */}</StatGroup>
<StatGroup columns={4} gap="lg">{/* ... */}</StatGroup>
<StatGroup columns={6} gap="md">{/* ... */}</StatGroup>
```

<ComponentPreview name="stat-columns-demo" />

### Row alignment

Stats in a group render on a shared CSS subgrid, so labels, values, and sublabels each occupy a common row. When one label wraps to two lines, its neighbors' values stay on the same baseline instead of drifting up — no fixed label heights needed.

<ComponentPreview name="stat-aligned-demo" />

## Props

### Stat

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | `string` | — | Caption shown above the value (uppercased, muted). |
| `value` | `React.ReactNode` | — | The metric value. |
| `sublabel` | `React.ReactNode` | — | Muted secondary line below the value. |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Value type scale. `lg` is bold with tight tracking. |
| `tone` | `'default' \| 'muted' \| 'success' \| 'warning' \| 'destructive'` | `'default'` | Value color. |
| `mono` | `boolean` | `false` | Use monospaced, tabular figures for the value. |
| `delta` | `{ value: string; direction: 'up' \| 'down' \| 'neutral'; tone? }` | — | Optional change indicator. |
| `labelLink` | `React.ReactElement` | — | Link element (no children) that the label renders as, with a trailing arrow. |
| `testId` | `string` | — | Maps to `data-testid`. |

Also accepts all `div` props.

### StatGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `columns` | `2 \| 3 \| 4 \| 6` | `4` | Column count at full width; steps down with the group's own width. |
| `gap` | `'sm' \| 'md' \| 'lg'` | `'md'` | Gap between stats. |
| `testId` | `string` | — | Maps to `data-testid`. |

Also accepts all `div` props. Children should be `Stat` items.
