Data Display

KPI

A key performance indicator card displaying a metric value with trend chip, sparkline chart, and contextual details.

Usage

Active Users
98K
3.3%

Anatomy

Import the KPI component and access all parts using dot notation.

import { KPI } from "@blakeui/pro-react";

<KPI>
  <KPI.Header>
    <KPI.Icon />
    <KPI.Title />
  </KPI.Header>
  <KPI.Actions />
  <KPI.Content>
    <KPI.Value />
    <KPI.Trend />
    <KPI.Progress />
  </KPI.Content>
  <KPI.Chart />
  <KPI.Separator />
  <KPI.Footer />
</KPI>;

With Actions

Impressions
128K
6.4%
API Requests
2.4M
1.9%

With Chart Bottom

Total Revenue
$228,451
8.2%

With Chart Inline

Only a chart that is a direct child of KPI goes full-bleed; nesting it inside KPI.Content keeps it in the value row, where it fills the grid's trailing auto column and needs an explicit width.

Sessions
48K
Total Subscribers
71,897
722

With Icon

Conversion Rate
4.7%
0.8%
Error Rate
2.1%
0.4%
Bounce Rate
42.3%
5.9%

With Progress

Storage Used
42%
CPU Load
74%
Error Budget
91%

CSS Classes

Base Classes

  • .kpi — The card shell. Relative, flex column, gap: 0, padding: --spacing * 4, border-radius: --radius * 2. Rendered on top of the @blakeui/react Card, whose default gap the gap: 0 neutralises so KPI can space its parts with margins instead.

Element Classes

  • .kpi__header — Top row. Flex, centred, gap: --spacing * 2, margin-bottom: --spacing * 1.
  • .kpi__content — The value row. grid-template-columns: 1fr auto, items centred, column-gap: --spacing * 2, flex: 1 — so the value takes the free space and the trend or chart sits in the trailing column.
  • .kpi__icon — Status tile. --spacing * 8 square, border-radius: --radius * 1, centred, and it sizes its own svg to --spacing * 4.
  • .kpi__actions — Absolutely positioned, inset calc(--spacing * 4 - --spacing * 1.5) from the top-right so the ghost button's own padding lines its glyph up with the card padding.
  • .kpi__title — 0.875rem/1.25rem, font-weight: 500, color: --muted. Renders as a dt.
  • .kpi__value — 1.5rem/2rem, font-weight: 600, letter-spacing: -0.025em, color: --foreground. Renders as a dd.
  • .kpi__trend — flex-shrink: 0, so the chip never compresses when the value is long.
  • .kpi__progress — Spans the whole value row (grid-column: 1 / -1) and adds --spacing * 1 above, dropping below the value rather than beside it.
  • .kpi__chart — Sparkline container. overflow: hidden plus a horizontal mask-image that fades to transparent over --kpi-chart-fade at each edge.
  • .kpi > .kpi__chart — Full-bleed treatment, applied only when the chart is a direct child of the root: margin-top: --spacing * 2, negative inline and bottom margins of --spacing * 4, and the two bottom corners rounded to --radius * 2. A chart nested anywhere else keeps the mask and nothing else.
  • .kpi > .kpi__chart:has(+ .kpi__separator, + .kpi__footer) — Drops the bottom bleed and the corner rounding when a separator or footer follows the chart.
  • .kpi__separator — width: calc(100% + --spacing * 8) with --spacing * -4 inline margins, so the rule reaches both card edges through the padding.
  • .kpi__footer — margin-bottom: --spacing * -1.
  • .kpi__separator + .kpi__footer — Removes the footer's top margin when a separator already provides the gap.

Data Attributes

  • [data-status="success"] on .kpi__icon — --success at 10% for the tile, --success for the glyph.
  • [data-status="warning"] on .kpi__icon — --warning at 10% for the tile, --warning for the glyph.
  • [data-status="danger"] on .kpi__icon — --danger at 10% for the tile, --danger for the glyph.
  • [data-slot] — Eleven of the twelve parts carry one (kpi, kpi-header, kpi-content, kpi-icon, kpi-title, kpi-value, kpi-progress, kpi-actions, kpi-chart, kpi-separator, kpi-footer), so a part can be targeted without depending on its BEM class.
  • [data-trend="up"] / [data-trend="down"] / [data-trend="neutral"] on .kpi__trend — Set by the underlying Chip. KPI.Trend is the one part with no kpi-trend slot: the Chip's own data-slot="chip" wins, so target the chip by .kpi__trend or by data-trend instead.

CSS Variables

  • --kpi-chart-fade — How far the sparkline mask fades in from each horizontal edge. Declared on .kpi__chart; default 10%.

API Reference

KPI

The root. Renders the @blakeui/react Card and supplies the slot context every part reads.

PropTypeDefaultDescription
childrenReactNode—The parts to compose. KPI.Chart is treated specially: as a direct child it goes full-bleed to the card edges, nested it stays inline.
classNamestring—Merged onto the .kpi root. Use it to flip the inherited Card elevation, e.g. border-separator border shadow-none for a flat tile.

Also accepts every BlakeUI Card prop.

KPI.Header

The top row — icon and title. Sits above KPI.Content, and is the only part KPI.Actions visually overlaps.

PropTypeDefaultDescription
childrenReactNode—Typically a KPI.Icon followed by a KPI.Title.
classNamestring—Merged onto .kpi__header.

Also accepts every native div attribute.

KPI.Content

The value row. A two-column grid (1fr auto) that pairs the value with whatever sits beside it.

PropTypeDefaultDescription
childrenReactNode—A KPI.Value plus one trailing part — a KPI.Trend or an inline KPI.Chart. A KPI.Progress spans both columns instead, dropping onto its own line. A third in-flow child wraps to an implicit second row.
classNamestring—Merged onto .kpi__content.

Also accepts every native div attribute.

KPI.Icon

The tinted glyph tile. Sizes any svg inside it, so an icon needs no size class of its own.

PropTypeDefaultDescription
status"success" | "warning" | "danger"—Sets data-status, which tints the tile with the matching token at 10% and colours the glyph with it at full strength. Omitted, the tile is untinted and the glyph inherits currentColor.
childrenReactNode—The glyph. Required.
classNamestring—Merged onto .kpi__icon.

Also accepts every native div attribute.

KPI.Title

The metric label, rendered as a dt.

PropTypeDefaultDescription
childrenReactNode—The label text. Required.
classNamestring—Merged onto .kpi__title.

Also accepts every native dt attribute.

KPI.Value

The formatted number, rendered as a dd. Wraps NumberValue, so formatting is locale-aware and driven entirely by props.

PropTypeDefaultDescription
valuenumber—The number to format. Required.
children(formatted: string) => ReactNode—Render function receiving the formatted string, for appending a unit or wrapping part of the value in its own markup. Without it the formatted string renders as-is.
style"decimal" | "currency" | "percent" | "unit""decimal"Formatting style. currency also needs currency; unit also needs unit. A percent value is a fraction, so 0.42 renders as 42%.
notation"standard" | "compact" | "scientific" | "engineering""standard"compact is what turns 98000 into 98K.
currencystring—ISO 4217 code, e.g. USD. Required by style="currency".
unitstring—Sanctioned unit identifier, e.g. celsius. Required by style="unit".
minimumFractionDigits / maximumFractionDigitsnumber—Clamp the decimals. Both follow the Intl defaults for the chosen style.
signDisplay"auto" | "always" | "never" | "exceptZero""auto"exceptZero forces a leading + on positive values.
localestring—Overrides the locale. Defaults to the ambient React Aria locale, so leave it unset to follow the app.
formatOptionsNumberFormatOptions—An escape hatch that replaces every option above rather than merging with them.
classNamestring—Merged onto .kpi__value.

KPI.Trend

The delta badge. Wraps TrendChip, whose direction drives both the caret and the colour.

PropTypeDefaultDescription
trend"up" | "down" | "neutral""up"Direction — not sentiment. up is a success caret, down a danger one, and neutral drops the caret for a default-coloured chip.
childrenReactNode—The delta text. Required. Accepts TrendChip.Prefix, TrendChip.Suffix, and TrendChip.Indicator alongside it.
variant"soft" | "primary" | "secondary" | "tertiary""soft"Chip fill treatment.
size"sm" | "md" | "lg""sm"Chip size, which also sizes the caret.
classNamestring—Merged onto .kpi__trend.

KPI.Progress

A full-width bar beneath the value. Wraps the @blakeui/react ProgressBar at size sm, whose fill transitions over 300ms when the value changes and holds still under prefers-reduced-motion.

PropTypeDefaultDescription
valuenumber—Percentage from 0 to 100. Required, and clamped to that range — it is independent of KPI.Value, so a percent-formatted value has to be passed to both.
status"success" | "warning" | "danger""success"Fill colour, mapped onto the ProgressBar colour of the same name.
classNamestring—Merged onto .kpi__progress, the wrapper — not onto the bar inside it.

Also accepts every native div attribute.

KPI.Actions

The corner button. Wraps the @blakeui/react Button as a small ghost icon-only button, absolutely positioned over the header.

PropTypeDefaultDescription
childrenReactNodeThree-dot glyphReplaces the default glyph. A replacement is not sized for you, so give it size-4 to match.
aria-labelstring—Pass one. The button is icon-only and the component supplies no fallback name, so without it the control is announced unlabelled.
onPress(e: PressEvent) => void—Press handler. For a menu, use the button as a Menu trigger instead.
classNamestring—Forwarded to the Button, not to the .kpi__actions wrapper — which is the one part whose positioning class cannot be overridden from the outside.

Also accepts every BlakeUI Button prop.

KPI.Chart

The sparkline. Full-bleed as a direct child of KPI, inline anywhere else, and edge-masked in both cases.

PropTypeDefaultDescription
dataRecord<string, number | string>[]—One object per point, each carrying a numeric field named by dataKey. Required.
dataKeystring"value"Which field of each object supplies the Y value.
colorstring"currentColor"Stroke colour, and the gradient's colour when fillColor is unset. Takes a var(--chart-1) reference.
fillColorstring—Overrides the gradient colour independently of the stroke. The gradient itself always runs 20% down to 2% opacity.
heightnumber80Height in pixels. The width is always fluid, so an inline chart also needs a width from className.
strokeWidthnumber2Line thickness.
classNamestring—Merged onto .kpi__chart.

Also accepts every native div attribute.

KPI.Separator

An edge-to-edge rule. Wraps the @blakeui/react Separator and widens it back through the card padding.

PropTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"Only horizontal is meaningful here — the full-bleed width comes from the KPI class, which assumes a horizontal rule.
classNamestring—Merged onto .kpi__separator.

Also accepts every BlakeUI Separator prop.

KPI.Footer

The bottom strip, for a link or a supporting detail.

PropTypeDefaultDescription
childrenReactNode—The footer content. Required.
classNamestring—Merged onto .kpi__footer.

Also accepts every native div attribute.

On this page