KPI Cards
Discover various KPI card layouts and features to highlight important metrics in your UI.
Color Variants
Showcase of KPI cards with different border colors to indicate status or category.
KPI Label
10 352
GB
KPI Label
10 352
GB
KPI Label
10 352
GB
KPI Label
10 352
GB
import { HvCard, HvCardContent, HvColor, HvIconContainer, HvTypography, } from "@hitachivantara/uikit-react-core"; const colors = ["positive", "warning", "negative", "info"] satisfies HvColor[]; export default function Demo() { return ( <div className="grid gap-sm grid-cols-1 sm:grid-cols-2 md:grid-cols-4"> {colors.map((color) => ( <HvCard key={color} statusColor={color} bgcolor="bgContainer"> <Kpi title="KPI Label" value="10 352" unit="GB" /> </HvCard> ))} </div> ); } function Kpi({ title, value, unit, }: { title: React.ReactNode; value: React.ReactNode; unit: React.ReactNode; }) { return ( <HvCardContent className="grid gap-sm pb-xs!"> <div className="flex items-center gap-xxs"> <HvIconContainer size="xs" className="p-4px rounded-round bg-bgContainerSecondary border border-borderSubtle" > <div className="i-ph-copy-simple" /> </HvIconContainer> <span>{title}</span> </div> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {unit} </HvTypography> </div> </HvCardContent> ); }
Selectable Cards
KPI cards with radio selection support—ideal for interactive dashboards or comparisons.
KPI Label
10 352
GB
KPI Label
10 352
GB
KPI Label
10 352
GB
KPI Label
10 352
GB
import { useId, useState } from "react"; import { HvCard, HvCardContent, HvColor, HvIconContainer, HvRadio, HvTypography, } from "@hitachivantara/uikit-react-core"; const colors = ["positive", "warning", "negative", "info"] satisfies HvColor[]; export default function Demo() { const [selectedIndex, setSelectedIndex] = useState(-1); return ( <div className="grid gap-sm grid-cols-1 sm:grid-cols-2 md:grid-cols-4"> {colors.map((color, i) => ( <HvCard key={color} statusColor={color} bgcolor="bgContainer" className="cursor-pointer" onClick={() => setSelectedIndex(i)} selectable selected={i === selectedIndex} > <Kpi title="KPI Label" value="10 352" unit="GB" selected={i === selectedIndex} onSelect={() => setSelectedIndex(i)} /> </HvCard> ))} </div> ); } function Kpi({ title, value, unit, selected, onSelect, }: { title: React.ReactNode; value: React.ReactNode; unit: React.ReactNode; selected: boolean; onSelect: () => void; }) { const titleId = useId(); return ( <HvCardContent className="grid gap-sm pb-xs!"> <div className="flex items-center gap-xxs"> <HvIconContainer size="xs" className="p-4px rounded-round bg-bgContainerSecondary border border-borderSubtle" > <div className="i-ph-copy-simple" /> </HvIconContainer> <span id={titleId}>{title}</span> </div> <div className="flex justify-between items-center"> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {unit} </HvTypography> </div> <HvRadio name="kpi" aria-labelledby={titleId} checked={selected} onClick={onSelect} /> </div> </HvCardContent> ); }
Dynamic KPI Value
Live KPI card that fetches and displays data based on a selected endpoint, including fallback/error handling.
Loading data...
import { useState } from "react"; import useSWR from "swr"; import { HvCard, HvCardContent, HvIconContainer, HvLoading, HvRadio, HvRadioGroup, HvTypography, } from "@hitachivantara/uikit-react-core"; export default function Demo() { const [endpoint, setEndpoint] = useState("1"); return ( <div className="grid gap-sm"> <HvRadioGroup orientation="horizontal" label="Choose endpoint" value={endpoint} onChange={(evt, val) => setEndpoint(val)} > <HvRadio label="Option 1" value="1" /> <HvRadio label="Option 2" value="2" /> <HvRadio label="Bad fetch" value="error" /> </HvRadioGroup> <HvCard statusColor="info" bgcolor="bgContainer" className="w-300px"> <HvCardContent className="grid gap-sm pb-xs! h-90px"> <KpiData id={endpoint} /> </HvCardContent> </HvCard> </div> ); } function KpiData({ id }: { id: string }) { const { data, isLoading, error } = useSWR(id, fetchData); if (isLoading) { return <HvLoading small label="Loading data..." className="flex-row" />; } if (error) { return ( <div className="flex gap-xs items-center justify-center text-negative"> <HvIconContainer className="rounded-4px bg-negativeDimmed border border-negativeBorder"> <div className="i-ph-warning-diamond" /> </HvIconContainer> <HvTypography>{error.message}</HvTypography> </div> ); } return ( <> <div className="flex items-center gap-xxs"> <HvIconContainer size="xs" className="p-4px rounded-round bg-bgContainerSecondary border border-borderSubtle" > <div className="i-ph-copy-simple" /> </HvIconContainer> <span>{data?.title}</span> </div> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{data?.value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {data?.unit} </HvTypography> </div> </> ); } function fetchData(id: string) { interface Data { title: string; value: number; unit: string; } return new Promise<Data>((resolve, reject) => { setTimeout(() => { if (Number.isNaN(Number(id))) { reject(new Error("Error fetching data")); return; } resolve({ title: `Title ${id}`, value: Number.parseInt(id.repeat(6), 8) % 10 ** 5, unit: "GB", }); }, 4000); }); }