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__title0.875rem/1.25rem, font-weight: 500, color: --muted. Renders as a dt.
  • .kpi__value1.5rem/2rem, font-weight: 600, letter-spacing: -0.025em, color: --foreground. Renders as a dd.
  • .kpi__trendflex-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__separatorwidth: calc(100% + --spacing * 8) with --spacing * -4 inline margins, so the rule reaches both card edges through the padding.
  • .kpi__footermargin-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
childrenReactNodeThe parts to compose. KPI.Chart is treated specially: as a direct child it goes full-bleed to the card edges, nested it stays inline.
classNamestringMerged 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
childrenReactNodeTypically a KPI.Icon followed by a KPI.Title.
classNamestringMerged 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
childrenReactNodeA 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.
classNamestringMerged 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.
childrenReactNodeThe glyph. Required.
classNamestringMerged onto .kpi__icon.

Also accepts every native div attribute.

KPI.Title

The metric label, rendered as a dt.

PropTypeDefaultDescription
childrenReactNodeThe label text. Required.
classNamestringMerged 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
valuenumberThe number to format. Required.
children(formatted: string) => ReactNodeRender 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.
currencystringISO 4217 code, e.g. USD. Required by style="currency".
unitstringSanctioned unit identifier, e.g. celsius. Required by style="unit".
minimumFractionDigits / maximumFractionDigitsnumberClamp the decimals. Both follow the Intl defaults for the chosen style.
signDisplay"auto" | "always" | "never" | "exceptZero""auto"exceptZero forces a leading + on positive values.
localestringOverrides the locale. Defaults to the ambient React Aria locale, so leave it unset to follow the app.
formatOptionsNumberFormatOptionsAn escape hatch that replaces every option above rather than merging with them.
classNamestringMerged 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.
childrenReactNodeThe 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.
classNamestringMerged 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
valuenumberPercentage 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.
classNamestringMerged 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-labelstringPass one. The button is icon-only and the component supplies no fallback name, so without it the control is announced unlabelled.
onPress(e: PressEvent) => voidPress handler. For a menu, use the button as a Menu trigger instead.
classNamestringForwarded 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.
fillColorstringOverrides 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.
classNamestringMerged 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.
classNamestringMerged onto .kpi__separator.

Also accepts every BlakeUI Separator prop.

KPI.Footer

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

PropTypeDefaultDescription
childrenReactNodeThe footer content. Required.
classNamestringMerged onto .kpi__footer.

Also accepts every native div attribute.

On this page