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
dataRelayDataPoint—Required. { value, timestamp, isLoading?, error? }
numericValuenumber—Numeric value for zone evaluation. Use when data.value is a formatted string
labelstring—Label text below the value
formatValuefunction—Custom display formatter: (value) => string. See formatValue
alertZonesAlertZone[][]Color zones — zone color applies to the value text
onZoneChangefunction—Called when value enters a new zone — see onZoneChange
borderRadiusnumber | string—number (px), "rounded", or "sharp"
borderColorstring"#e0e0e0"Card border color
borderThicknessnumber—Card border width in px
stylesobject—Custom styling — see Styles
showLastUpdatedbooleanfalseShow the timestamp from data.timestamp
formatTimestampfunction—Custom timestamp formatter: (ts) => string
lastUpdatedMarginnumber8Margin above the timestamp in px
showLoadingbooleantrueShow skeleton shimmer when data.isLoading is true
onErrorfunction—Called 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 | number—Fixed width override
heightstring | number—Fixed 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
/>
);
}