Skip to content
Nizivo UI

KpiTile

One number, its change and its shape, on Card's surface. Tint a row of related KPIs with tone, which no longer follows the trend. The Suspense fallback is the same tile with status="loading".

Server component, no client JS

Import

import { KpiTile } from '@nizivo/react/kpi-tile';

// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css and @nizivo/react/css/card.css and @nizivo/react/css/delta.css and @nizivo/react/css/inline-error.css and @nizivo/react/css/metric.css and @nizivo/react/css/number-value.css and @nizivo/react/css/skeleton.css
import '@nizivo/react/css/kpi-tile.css';

Static or interactive

Server. It ships no client JavaScript when a Server Component renders it, in every Next.js build mode; its code is bundled only when a client component (a 'use client' file) imports it. The chart arrives as an element (chart={<Sparkline … />} from @nizivo/charts, which ships no client code either); @nizivo/react never imports @nizivo/charts. How to choose.

In a client app (a Vite or React Router single-page app, or a 'use client' file) it is an ordinary React component: event handlers, refs and state work as usual. In a client app.

See Dashboard for the status contract, a Suspense example and charts through slots.

Tones

tone is surface (the default), brand or accent: Card's surfaces, chosen for the row, not from the sign of the change.

  • On brand and accent the Delta pill is bg/surface rather than Figma's bg/success-subtle, which is the same green-50 as the brand tint and would show no fill. A deviation from Figma 04 (gap 4), chosen by the maintainer (M42); see Card.
  • In dark mode brand and accent are both green-950, so they look the same. That is Figma's design, followed for now (Figma 04 issue 5; M42).
  • The tile is a Card, so it declares --nz-surface: a Sparkline in chart draws its end-dot halo in the tile's colour, not as a white ring (Card).

Examples

Basic

Figma's four variants: the tint groups a row and no longer follows the trend.

Total sales
$48,290
+12.4%vs last week
Conversion
3.2%
-3.1%vs last week
Orders
1,284
+12.4%vs last week
Returning rate
28.4%
-3.1%vs last week
import type { JSX } from 'react';
import { KpiTile } from '@nizivo/react/kpi-tile';

const USD = { style: 'currency', currency: 'USD', maximumFractionDigits: 0 } as const;

/**
 * A stand-in for `<Sparkline data={series} label="…" />` from @nizivo/charts, which @nizivo/react
 * never imports: any element fits the `chart` slot.
 */
function Shape({ d }: { d: string }): JSX.Element {
  return (
    <svg viewBox="0 0 88 28" width="88" height="28" aria-hidden="true" fill="none">
      <path d={d} stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
    </svg>
  );
}

const UP = 'M1 22 L12 19 L23 21 L34 15 L45 17 L56 11 L67 12 L78 6 L87 4';

const DOWN = 'M1 6 L12 9 L23 7 L34 13 L45 11 L56 16 L67 15 L78 21 L87 23';

export function KpiTileExample(): JSX.Element {
  return (
    <div>
      <KpiTile
        label="Total sales"
        value={48290}
        format={USD}
        delta={0.124}
        comparison="vs last week"
        chart={<Shape d={UP} />}
      />
      <KpiTile
        label="Conversion"
        value={0.032}
        format={{ style: 'percent', maximumFractionDigits: 1 }}
        delta={-0.031}
        comparison="vs last week"
        chart={<Shape d={DOWN} />}
      />
      <KpiTile
        tone="brand"
        label="Orders"
        value={1284}
        delta={0.124}
        comparison="vs last week"
        chart={<Shape d={UP} />}
      />
      <KpiTile
        tone="accent"
        label="Returning rate"
        value={0.284}
        format={{ style: 'percent', maximumFractionDigits: 1 }}
        delta={-0.031}
        comparison="vs last week"
        chart={<Shape d={DOWN} />}
      />
    </div>
  );
}

States

The four statuses (§16.5). The loading tile is also the Suspense fallback; the box is the same.

Total sales
Loading Total sales
Total sales
No data
Total sales
Could not load Total sales
Total sales
$48,290
+12.4%
import type { JSX } from 'react';
import { KpiTile } from '@nizivo/react/kpi-tile';

const USD = { style: 'currency', currency: 'USD', maximumFractionDigits: 0 } as const;

export function KpiTileStatesExample(): JSX.Element {
  return (
    <div>
      <KpiTile label="Total sales" status="loading" />
      <KpiTile label="Total sales" value={null} />
      <KpiTile label="Total sales" status="error" />
      <KpiTile label="Total sales" value={48290} format={USD} delta={0.124} pending />
    </div>
  );
}

Props

One number, its change and its shape. A role="group" named by the label (not a landmark). The Suspense fallback is the same tile with status="loading": the box does not change (§16.5).

Server component Renders data-slot="kpi-tile" with the class nz-kpi-tile. Props: KpiTileProps. Source: src/kpi-tile/kpi-tile.tsx.

NameTypeDefaultDescription
chartReactNode–

A small chart beside the value, shown while ready, for example <Sparkline data={series} label="Sales, last 7 days" /> from @nizivo/charts. Drawn at most 88 × 28 px (Figma).

comparisonReactNode–

Visible text after the delta, for example "vs last week".

From MetricDataProps.

deltanumber | ReactElement–

The change. A number renders <Delta value={delta} locale={locale} /> (a fraction: 0.124 is "+12.4%"); pass your own <Delta … /> element for another format, a tone or an icon.

From MetricDataProps.

emptyReactNode–

Replaces the "—" of the empty state.

From MetricDataProps.

errorReactNode–

Replaces the InlineError of the error state, for example one with a Retry action.

From MetricDataProps.

formatIntl.NumberFormatOptions{}

Intl.NumberFormat options for the value.

From MetricDataProps.

labelrequiredReactNode–

The name of the number, for example "Total sales". Also names the status texts.

From MetricDataProps.

labelsPartial<MetricLabels>–

From MetricDataProps.

localestring'en-US'

BCP 47 tag for the value and a numeric delta.

From MetricDataProps.

pendingboolean–

A refresh is in flight while the value shows: data-pending and aria-busy.

From MetricDataProps.

renderRenderProp<KpiTileState>–
statusDataStatus
"ready""loading""empty""error"
'ready'

§16.5 lifecycle.

From MetricDataProps.

toneKpiTileTone
"surface""brand""accent"
'surface'

The surface; group a row of related KPIs with one tint.

valuenumber | null–

The number. null or undefined (or NaN) with status="ready" renders the empty state.

From MetricDataProps.

  • Also takes every attribute of <div> except children. (Omit<ComponentPropsWithRef<"div">, "children">)

render takes an element, or, in client code, a function of the props and this state (KpiTileState):

NameTypeDefaultDescription
pendingrequiredboolean–
statusrequiredDataStatus
"ready""loading""empty""error"
–

The status as rendered: 'empty' when a ready tile has no value.

tonerequiredKpiTileTone
"surface""brand""accent"
–

Other exports

KpiTileProps type

import type { KpiTileProps } from '@nizivo/react/kpi-tile'

The props of KpiTile, listed above.

KpiTileState type

import type { KpiTileState } from '@nizivo/react/kpi-tile'

The render state of KpiTile, listed above.

KpiTileTone type

import type { KpiTileTone } from '@nizivo/react/kpi-tile'

Figma "Tone": Surface, or Tinted as brand or accent (Card's tones of the same names).

type KpiTileTone = "surface" | "brand" | "accent"

Data attributes and slots

Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.

SlotRendered byData attributes
kpi-tile
.nz-kpi-tile
KpiTile
root (takes render)
  • data-padding: "sm"
    The padding prop, or the fixed padding of a part drawn on Card's surface, which has no padding prop; values in each component manifest (Card: sm, md; KpiTile: sm; TrendCard: md)
  • data-tone: "surface""brand""accent" ← tone
    The tone prop; values in each component manifest

CSS hooks

Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.

This stylesheet reads no public hook.

Layers: nz.components. Classes: .nz-card, .nz-kpi-tile, .nz-metric, .nz-metric-change.

Accessibility

Nothing in this component takes focus or keyboard input.

Keyboard

None.

Figma

Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.

On this page