import { MetricCard, MetricCardGroup } from "@cloudflare/kumo";
export function MetricCardGroupDemo() {
return (
<MetricCardGroup title="Activity">
<MetricCard
label="Web traffic"
value="2.4M"
trend={{ direction: "up", label: "12%" }}
sparkline={{
data: [
1.69, 1.62, 1.79, 1.98, 1.99, 1.89, 1.9, 2.07, 2.16, 2.04, 1.87,
1.91, 2.14, 2.3, 2.23, 2.13, 2.19, 2.34, 2.36, 2.21, 2.11, 2.26,
2.5, 2.58,
],
}}
/>
<MetricCard
label="Total bandwidth"
value="1.8"
unit="TB"
trend={{ direction: "up", label: "9%" }}
sparkline={{
data: [
1.25, 1.29, 1.24, 1.24, 1.34, 1.42, 1.38, 1.28, 1.3, 1.44, 1.55,
1.54, 1.48, 1.51, 1.61, 1.65, 1.58, 1.53, 1.6, 1.76, 1.83, 1.78,
1.72, 1.77,
],
}}
/>
<MetricCard
label="Cache rate"
value="92.1"
unit="%"
trend={{ direction: "up", label: "3.2%" }}
/>
<MetricCard
label="Client errors"
value="4.8k"
trend={{ direction: "down", label: "8%", lessIsBetter: true }}
/>
</MetricCardGroup>
);
} Installation
Barrel
import { MetricCard, MetricCardGroup } from "@cloudflare/kumo";Granular
import { MetricCard } from "@cloudflare/kumo/components/metric-card";
import { MetricCardGroup } from "@cloudflare/kumo/components/metric-card-group"; Usage
import { MetricCard, MetricCardGroup } from "@cloudflare/kumo";
export default function Example() {
return (
<MetricCardGroup title="Overview">
<MetricCard label="Requests" value="1.2M" />
<MetricCard label="Error Rate" value="0.3" unit="%" />
</MetricCardGroup>
);
} Examples
Horizontal Group
The default layout arranges metric cards in a horizontal row with vertical dividers.
import { MetricCard, MetricCardGroup } from "@cloudflare/kumo";
export function MetricCardHorizontalGroupDemo() {
return (
<MetricCardGroup title="Performance">
<MetricCard
label="CPU time P90"
value="3.2"
unit="ms"
trend={{ direction: "down", label: "8%", lessIsBetter: true }}
sparkline={{
data: [
3.91, 4.36, 4.41, 4.02, 3.74, 3.88, 4.08, 3.87, 3.44, 3.36,
3.77, 4.12, 3.94, 3.47, 3.3, 3.52, 3.64, 3.36, 3.04, 3.16,
3.61, 3.76, 3.37, 2.9,
],
}}
/>
<MetricCard
label="Workers errors"
value="0"
trend={{ direction: "neutral", label: "0.0%", isNeutral: true }}
sparkline={{
data: [0, 0],
theme: "danger",
}}
/>
<MetricCard
label="Workers invocations"
value="1.2M"
trend={{ direction: "up", label: "12%" }}
/>
</MetricCardGroup>
);
} Vertical Group
Use orientation="vertical" for sidebar or narrow container layouts.
import { MetricCard, MetricCardGroup } from "@cloudflare/kumo";
export function MetricCardGroupVerticalDemo() {
return (
<div className="max-w-sm mx-auto">
<MetricCardGroup title="Registrar" orientation="vertical">
<MetricCard label="Active domains" value="142" />
<MetricCard label="Expiring soon" value="3" />
<MetricCard label="Pending transfers" value="2" />
</MetricCardGroup>
</div>
);
} Without Title
Omit the title prop for a minimal layout without a header.
import { MetricCard, MetricCardGroup } from "@cloudflare/kumo";
export function MetricCardGroupWithoutTitleDemo() {
return (
<MetricCardGroup>
<MetricCard
label="Web traffic"
value="2.4M"
sparkline={{
data: [
1.86, 1.88, 1.75, 1.7, 1.88, 2.11, 2.14, 1.97, 1.88, 1.98,
2.13, 2.11, 1.99, 2.02, 2.24, 2.41, 2.33, 2.14, 2.11, 2.27,
2.4, 2.35, 2.26, 2.36,
],
}}
/>
<MetricCard
label="Total bandwidth"
value="842"
unit="GB"
sparkline={{
data: [
682.96, 643.68, 599.64, 616.3, 668.98, 685.79, 657.11, 649.29,
702.63, 765.45, 763.99, 708.53, 681.15, 721.75, 776.34, 780.2,
752.18, 762.21, 821.91, 861.85, 831.43, 775.95, 776.53, 838.01,
],
}}
/>
<MetricCard
label="Workers invocations"
value="1.2M"
/>
</MetricCardGroup>
);
} Sparklines
Add sparkline visualizations with themed or custom colors.
import { MetricCard, MetricCardGroup } from "@cloudflare/kumo";
export function MetricCardSparklineDemo() {
return (
<MetricCardGroup title="Performance">
<MetricCard
label="Cache rate"
value="92.1"
unit="%"
trend={{ direction: "up", label: "3.2%" }}
sparkline={{
data: [
83.96, 88.56, 90.5, 87.29, 84.93, 88.62, 94.47, 94.59, 88.45,
83.97, 86.51, 91.74, 92.37, 88.79, 88, 92.65, 96.59, 93.67,
87.08, 85.28, 90.31, 95.21, 94.12, 90.4,
],
theme: "success",
}}
/>
<MetricCard
label="Client errors"
value="4.8k"
trend={{ direction: "down", label: "8%", lessIsBetter: true }}
sparkline={{
data: [
6138.54, 5544.96, 5909.1, 6819.45, 6923.97, 5877.53, 4895.88,
5081.88, 5925.52, 6120.88, 5470.26, 5090.26, 5657.14, 6317.94,
5908.69, 4709.82, 4143.49, 4805.35, 5660.03, 5546.95, 4795.58,
4623.79, 5256.64, 5567.25,
],
theme: "danger",
}}
/>
<MetricCard
label="CPU time P90"
value="3.2"
unit="ms"
trend={{ direction: "down", label: "8%", lessIsBetter: true }}
sparkline={{
data: [
4.4, 4.5, 4.08, 3.59, 3.58, 3.95, 4.1, 3.84, 3.6, 3.77, 4.07,
3.96, 3.44, 3.1, 3.33, 3.73, 3.75, 3.41, 3.24, 3.47, 3.64,
3.35, 2.86, 2.76,
],
theme: "neutral",
}}
/>
<MetricCard
label="Uptime"
value="99.99"
unit="%"
trend={{ direction: "neutral", label: "0.0%", isNeutral: true }}
sparkline={{
data: [
99.98, 99.98, 99.99, 99.99, 99.98, 99.99, 99.99, 99.99, 99.98,
99.99, 99.99, 99.99, 99.99, 99.98, 99.99, 99.99, 99.99, 99.99,
99.98, 99.99, 99.99, 99.99, 99.99, 99.99,
],
yMin: 99.9,
}}
/>
</MetricCardGroup>
);
} Interactive Cards
Cards become clickable when given an href or onClick prop. Use tooltip
to add contextual information.
import { MetricCard, MetricCardGroup } from "@cloudflare/kumo";
import { QuestionIcon } from "@phosphor-icons/react";
export function MetricCardInteractiveDemo() {
return (
<MetricCardGroup title="Workers Overview">
<MetricCard
label="Workers invocations"
value="1.2M"
tooltip="Total invocations across all Workers"
href="#"
/>
<MetricCard
label="CPU time P90"
value="3.2"
unit="ms"
tooltip="90th percentile CPU time per invocation"
tooltipIcon={QuestionIcon}
href="#"
/>
<MetricCard
label="Workers errors"
value="842"
tooltip="Failed invocations returning non-2xx status"
onClick={() => {}}
/>
</MetricCardGroup>
);
} Loading and Error States
MetricCard supports loading and error states for async data.
import { MetricCard, MetricCardGroup } from "@cloudflare/kumo";
export function MetricCardStatesDemo() {
return (
<MetricCardGroup title="Browser Rendering">
<MetricCard label="Browser sessions" loading />
<MetricCard label="Browser hours" error />
</MetricCardGroup>
);
} Badge Values
Pass a ReactNode like Badge as the value prop for
status indicators. ReactNode values render with their own styling. String values
automatically receive metric typography.
import { MetricCard, MetricCardGroup, Badge } from "@cloudflare/kumo";
export function MetricCardBadgeValueDemo() {
return (
<MetricCardGroup title="System Status">
<MetricCard
label="API"
value={<Badge variant="success">Operational</Badge>}
/>
<MetricCard
label="Database"
value={<Badge variant="warning">Degraded</Badge>}
/>
<MetricCard label="Uptime" value="99.9" unit="%" />
</MetricCardGroup>
);
} API Reference
MetricCard
Compact card displaying a single metric value with optional trend, sparkline, and loading/error states.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | - |
| id | string | - | - |
| title | string | - | - |
| children | ReactNode | - | - |
| label* | string | - | Label text displayed above the value. |
| value | string | ReactNode | - | Metric value to display. Strings and numbers receive automatic metric typography (text-xl, font-semibold, tabular-nums). ReactNode values like Badge render as-is. Optional when `loading` or `error` is true. |
| unit | string | - | Unit displayed after the value in smaller text, e.g. "%", "ms". |
| trend | MetricCardTrend | - | Trend indicator with direction arrow and label. |
| sparkline | MetricCardSparkline | - | Sparkline chart rendered at the bottom of the card. |
| href | string | - | When set, renders the card as an anchor element. |
| loading | boolean | - | Shows skeleton loading state for the value. |
| error | boolean | - | Shows an em-dash placeholder in place of the value. |
| tooltip | ReactNode | - | Content displayed in a tooltip next to the label. |
| tooltipIcon | React.ComponentType<MetricCardTooltipIconProps> | - | Custom icon component for the tooltip trigger. Defaults to Info icon from Phosphor. |
| onClick | (event: React.MouseEvent<HTMLElement>) => void | - | Click handler. Renders the card as a button when no `href` is provided. |
The trend and sparkline props accept configuration objects described below.
Trend Options
Options for the trend prop.
| Prop | Type | Default | Description |
|---|---|---|---|
| direction* | "up" | "down" | "neutral" | - | Arrow direction: up (↑), down (↓), or neutral (→). |
| label* | string | - | Pre-formatted trend label, e.g. "13.8%", "2.3pp", "5.1ms". |
| lessIsBetter | boolean | false | Inverts color semantics: down becomes green (good), up becomes red (bad). Use for metrics like latency or error count. |
| isNeutral | boolean | false | Forces neutral (gray) styling regardless of direction. Use for metrics where change has no good/bad meaning. |
Sparkline Options
Options for the sparkline prop.
| Prop | Type | Default | Description |
|---|---|---|---|
| data* | number[] | - | Array of numeric data points to plot. |
| theme | "success" | "danger" | "neutral" | Brand color | Preset color theme for the sparkline. For custom colors beyond these presets, use the color prop instead. |
| color | string | - | Custom color that overrides theme. Accepts any CSS color value including kumo tokens, e.g. "var(--color-kumo-brand)" or "#E87B35". |
| yMin | number | Auto from data | Override the auto-scaled minimum y value. |
| yMax | number | Auto from data | Override the auto-scaled maximum y value. |
MetricCardGroup
Groups MetricCard children inside a card with an optional header and configurable orientation.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | - |
| id | string | - | - |
| children | ReactNode | - | MetricCard children to render inside the group. |
| orientation | "horizontal" | "vertical" | "horizontal" | Layout direction for child metric cards. - `"horizontal"` — Cards in a row with vertical dividers - `"vertical"` — Cards stacked with horizontal dividers |
| title | ReactNode | - | Optional header text rendered in a LayerCard.Secondary section. |