Skip to main content

Stat Card

A card that displays a single value with optional label, timestamp, and zone-aware coloring. Scales responsively with its container.

Playground

info

onZoneChange callbacks work in your local project but not in this interactive playground due to how the editor re-renders components.

Props

PropTypeDefaultDescription
dataRelayDataPointRequired. { value, timestamp, isLoading?, error? }
numericValuenumberNumeric value for zone evaluation. Use when data.value is a formatted string
labelstringLabel text below the value
formatValuefunctionCustom display formatter: (value) => string. See formatValue
alertZonesAlertZone[][]Color zones — zone color applies to the value text
onZoneChangefunctionCalled when value enters a new zone — see onZoneChange
borderRadiusnumber | stringnumber (px), "rounded", or "sharp"
borderColorstring"#e0e0e0"Card border color
borderThicknessnumberCard border width in px
stylesobjectCustom styling — see Styles
showLastUpdatedbooleanfalseShow the timestamp from data.timestamp
formatTimestampfunctionCustom timestamp formatter: (ts) => string
lastUpdatedMarginnumber8Margin above the timestamp in px
showLoadingbooleantrueShow skeleton shimmer when data.isLoading is true
onErrorfunctionCalled on invalid data — see onError

formatValue

Controls how data.value is displayed. Without it, the raw value is shown.

// append unit
formatValue={(v) => `${v}°C`}

// round
formatValue={(v) => Math.round(v).toLocaleString()}

// status text
formatValue={(v) => v > 80 ? "HOT" : "OK"}

Zone Color Behavior

When alert zones are set, the value text color changes to match the active zone. The label and timestamp colors are not affected unless overridden via styles.


Styles

styles={{
value: { fontSize: 32, fontWeight: 700, color: "#1e293b", fontFile: "/fonts/Custom.woff2" },
label: { fontSize: 13, fontWeight: 400, color: "#6b7280" },
lastUpdated: { fontSize: 11, fontWeight: 400, color: "#9ca3af" },
background: { color: "#ffffff" },
width: 200,
height: 120,
}}
FieldApplies toProperties
valueThe large value displayfontFamily, fontSize, fontWeight, color, fontFile
labelLabel text below valuefontFamily, fontSize, fontWeight, color, fontFile
lastUpdatedTimestamp textfontFamily, fontSize, fontWeight, color, fontFile
backgroundCard backgroundcolor
FieldTypeDefaultDescription
widthstring | numberFixed width override
heightstring | numberFixed height override
info

Explicit styles.value.color overrides zone coloring. If you want zone-aware colors, don't set styles.value.color.


With Hooks

import { useRelayLatest, StatCard } from "@relay-x/ui";

function TemperatureCard() {
const latest = useRelayLatest({
deviceIdent: "sensor_01",
metric: "temperature",
timeRange: { start: Date.now() - 86400000, end: Date.now() },
});

return (
<StatCard
data={latest}
label="Temperature"
formatValue={(v) => `${v}°C`}
showLastUpdated
/>
);
}