M
MetricUI
Guide

Cookbook

Recipes for every “I didn't know you could do that” moment. Each recipe is live — what you see is what you get.

Tables That Do Everything

DataTable columns have a type field that transforms plain data into rich UI. No render functions needed.

Sparklines in cells

Metric7-Day Trend
Revenue
Users
<DataTable
  data={[
    { name: "Revenue", trend: [12, 18, 14, 22, 19, 26, 31] },
    { name: "Users",   trend: [80, 85, 78, 92, 95, 88, 102] },
  ]}
  columns={[
    { key: "name", header: "Metric" },
    { key: "trend", header: "7-Day Trend", type: "sparkline" },
  ]}
/>

Status dots in cells

ServiceHealth
APIHealthy
DatabaseDegraded
CDNDown
<DataTable
  data={[
    { service: "API",      uptime: 99.98 },
    { service: "Database", uptime: 97.2 },
    { service: "CDN",      uptime: 89.1 },
  ]}
  columns={[
    { key: "service", header: "Service" },
    { key: "uptime",  header: "Health", type: "status",
      statusRules: [
        { min: 99.9, color: "emerald", label: "Healthy" },
        { min: 95,   color: "amber",   label: "Degraded", pulse: true },
        {            color: "red",     label: "Down",     pulse: true },
      ],
    },
  ]}
  dense
/>

Inline bar fills

ServerCPU Usage
Acme Corp
92%
Globex
67%
Initech
34%
Umbrella
88%
Cyberdyne
12%
{ key: "usage", header: "CPU", type: "bar", format: "percent",
  conditions: [
    { when: "at_or_above", value: 90, color: "red" },
    { when: "at_or_above", value: 70, color: "amber" },
    { when: "below", value: 70, color: "emerald" },
  ]
}

Expandable detail rows

Click the chevron to expand a row and reveal nested components — charts, stats, anything.

CustomerRevenueStatus
Acme Corp$84.2Kactive
Globex$62.1Kactive
Initech$31.4Ktrial
<DataTable
  data={customers}
  columns={columns}
  renderExpanded={(row) => (
    <div className="grid grid-cols-3 gap-3">
      <KpiCard bare title="Revenue" value={row.revenue} format="currency" />
      <KpiCard bare title="Completion" value={row.completion} format="percent" />
      <Sparkline data={row.trend} height={48} />
    </div>
  )}
/>

Hierarchical rows

RegionRevenue
North America$1.2M
US East$450.0K
US West$380.0K
Canada$370.0K
Europe$800.0K
UK$350.0K
Germany$280.0K
France$170.0K
<DataTable
  data={[
    { region: "North America", revenue: 1200000, children: [
      { region: "US East", revenue: 450000 },
      { region: "US West", revenue: 380000 },
      { region: "Canada", revenue: 370000 },
    ]},
  ]}
  childrenField="children"
  defaultExpanded
/>

Row highlighting

AccountRevenueChurnGrowth
Acme Corp$84.2K0%0%
Globex$62.1K0%0%
Initech$31.4K0%-0%
Umbrella$95.6K0%0%
Cyberdyne$18.9K0%-0%
<DataTable
  data={accounts}
  rowConditions={[
    { when: (row) => row.churn > 0.15,  className: "bg-red-500/5" },
    { when: (row) => row.growth > 0.30, className: "bg-emerald-500/5" },
  ]}
/>

KPI Cards Beyond Basics

These recipes show what most devs discover weeks into using MetricUI.

Multiple comparisons

Revenue
$0
+12.3%vs last month
+$29.3Kvs last year
<KpiCard
  title="Revenue"
  value={127450}
  format="currency"
  comparison={[
    { value: 113500, label: "vs last month" },
    { value: 98200, label: "vs last year", mode: "absolute" },
  ]}
/>

Down is good

Churn Rate
0%
-33.3%
<KpiCard
  title="Churn Rate"
  value={3.2}
  format="percent"
  icon={<TrendingDown className="h-3.5 w-3.5" />}
  comparison={{ value: 4.8, invertTrend: true }}
/>

Goal tracking

Q2 Revenue
$0
Q2 Target($500.0K)$120.0K left
<KpiCard
  title="Q2 Revenue"
  value={380000}
  format="currency"
  icon={<DollarSign className="h-3.5 w-3.5" />}
  goal={{
    value: 500000,
    label: "Q2 Target",
    showTarget: true,
    showRemaining: true,
    color: "#6366F1",
    completeColor: "#10B981",
  }}
/>

Previous period sparkline

Daily Revenue
$0
<KpiCard
  title="Daily Revenue"
  value={12400}
  format="currency"
  sparkline={{
    data: thisWeek,
    previousPeriod: lastWeek,
    type: "bar",
  }}
/>

Conditional glow

The card gets a colored glow when the value hits a threshold.

Error Rate
0%
Error Rate
0%
<KpiCard
  title="Error Rate"
  value={12.4}
  format="percent"
  conditions={[
    { when: "at_or_above", value: 10, color: "red" },
    { when: "at_or_above", value: 5,  color: "amber" },
    { when: "below",       value: 5,  color: "emerald" },
  ]}
/>

Charts That Tell Stories

Overlay targets, thresholds, and dual axes to turn a chart into a narrative.

Target lines

<AreaChart
  data={data}
  index="month"
  categories={["revenue"]}
  format="currency"
  referenceLines={[
    { axis: "y", value: 70000, label: "Target", color: "#6366F1", style: "dashed" },
    { axis: "y", value: 50000, label: "Break-even", color: "#EF4444" },
  ]}
  enableArea gradient
/>

Threshold zones

<AreaChart
  data={data}
  index="month"
  categories={["orders"]}
  thresholds={[
    { from: 0,   to: 350, color: "#EF4444", opacity: 0.06, label: "Low" },
    { from: 350, to: 450, color: "#F59E0B", opacity: 0.06, label: "Normal" },
    { from: 450, to: 600, color: "#10B981", opacity: 0.06, label: "High" },
  ]}
/>

Dual Y-axis

<AreaChart
  data={data}
  index="month"
  categories={[
    { key: "revenue", format: "currency" },
    { key: "orders", format: "number", axis: "right" },
  ]}
  rightAxisLabel="Orders"
  enableArea gradient
/>

100% stacked

Traffic Mix
<AreaChart
  data={channelData}
  index="month"
  categories={["organic", "paid", "referral", "direct"]}
  stacked
  stackMode="percent"
  format="percent"
  title="Traffic Mix"
/>

Per-series styling

<AreaChart
  data={data}
  index="month"
  categories={["revenue", "costs"]}
  format="currency"
  seriesStyles={{
    revenue: { lineWidth: 2.5 },
    costs:   { lineStyle: "dashed", lineWidth: 1.5 },
  }}
/>

Target vs actual bars

Deals Closed vs Target
<BarChart
  data={actuals}
  index="rep"
  categories={["closed"]}
  targetData={targets}
  title="Deals Closed vs Target"
/>

Layout Without CSS

Drop components into MetricGrid. KPIs row up, charts split intelligently, tables go full-width. No grid classes.

Revenue
$0
Users
0
Conversion
0%

Trends

<MetricGrid>
  <KpiCard title="Revenue" value={127450} format="currency" />
  <KpiCard title="Users" value={8420} format="number" />
  <KpiCard title="Conversion" value={4.8} format="percent" />

  <MetricGrid.Section title="Trends" />

  <AreaChart data={data} index="month" categories={["revenue"]} />
  <DonutChart data={plans} index="plan" categories={["users"]} />
</MetricGrid>

Data-Driven Alerts

Callout evaluates rules against a value and picks the variant automatically.

<Callout
  value={12.4}
  rules={[
    { min: 10, variant: "error",
      title: "Critical: {value}% error rate",
      message: "Immediate action required." },
    { min: 5, variant: "warning",
      title: "Elevated: {value}% error rate" },
    { variant: "success",
      title: "Error rate normal" },
  ]}
  metric={{ value: 12.4, format: "percent", label: "current" }}
  dismissible dense
/>

Live Ops Patterns

StatusIndicator at size="card" matches KpiCard and slots into MetricGrid.

API Gateway

Healthy

↑ Risingfor 4h
Database

Degraded

CDN

Healthy

<MetricGrid columns={3}>
  <StatusIndicator
    size="card"
    title="API Gateway"
    value={99.97}
    rules={[
      { min: 99.9, color: "emerald", label: "Healthy" },
      { min: 95,   color: "amber",   label: "Degraded", pulse: true },
      {            color: "red",     label: "Down",     pulse: true },
    ]}
    since={new Date(Date.now() - 3600000 * 4)}
    trend={[99.1, 99.5, 99.8, 99.9, 99.97]}
  />
  <StatusIndicator size="card" title="Database" value={98.2}
    rules={[
      { min: 99.9, color: "emerald", label: "Healthy" },
      { min: 95, color: "amber", label: "Degraded", pulse: true },
      { color: "red", label: "Down", pulse: true },
    ]}
  />
  <StatusIndicator size="card" title="CDN" value={99.99}
    rules={[
      { min: 99.9, color: "emerald", label: "Healthy" },
      { min: 95, color: "amber", label: "Degraded", pulse: true },
      { color: "red", label: "Down", pulse: true },
    ]}
  />
</MetricGrid>

Feature Enablement Matrix

Use DataTable with custom render functions to build feature comparison matrices — plans vs features, customers vs capabilities, any two-dimensional mapping.

FeatureStarterProEnterprise
Dashboard Builder

Drag-and-drop layout editor

Custom Charts

Build charts from any data source

Team Sharing
API Access

REST & GraphQL endpoints

SSO / SAML
Audit Log
Limited
Custom Branding
SLA

Uptime guarantee

Limited
Dedicated Support
<DataTable
  data={features}
  columns={[
    { key: "feature", header: "Feature", pin: "left",
      render: (v, row) => (
        <div>
          <span className="font-medium">{v}</span>
          {row.description && (
            <p className="text-xs text-[var(--muted)]">{row.description}</p>
          )}
        </div>
      ),
    },
    ...["Starter", "Pro", "Enterprise"].map((plan) => ({
      key: plan.toLowerCase(),
      header: plan,
      align: "center",
      render: (v) =>
        v === true  ? <Check className="text-emerald-500" /> :
        v === "limited" ? <Badge color="amber" size="sm">Limited</Badge> :
                          <Minus className="text-[var(--muted)]" />,
    })),
  ]}
/>

SaaS tenant matrix

Internal ops pattern — a tenant-by-module rollout matrix with icon status indicators and expandable detail rows. Great for CSM dashboards, feature rollout tracking, and adoption monitoring.

Studio Module Rollout

12 studios · 5 modules

StudioAcct MgrRoomsSchedulingBillingRetentionReportsAPI
Iron TempleENT
Nora Voss38
CorePulse FitnessMid
Nora Voss12
Zenith YogaSMB
Leo Park2
Lift LabENT
Nora Voss24
Barre & BeyondMid
Leo Park6
SweatBoxSMB
Self Service1
Peak PerformanceENT
Nora Voss31
Cycle SocietyMid
Leo Park8
FlexPoint StudiosSMB
Self Service3
Grind AthleticsMid
Leo Park10
Live In progress Setting up Not enabled
const renderStage = (v: unknown) => {
  const s = String(v);
  if (s === "R") return <CheckCircle2 className="mx-auto h-4 w-4 text-emerald-500" />;
  if (s === "D") return <Loader className="mx-auto h-4 w-4 text-amber-500" />;
  if (s === "S") return <Wrench className="mx-auto h-4 w-4 text-blue-500" />;
  return <Minus className="mx-auto h-4 w-4 text-[var(--muted)] opacity-30" />;
};

const modules = ["Scheduling", "Billing", "Retention", "Reports", "API"];

<DataTable
  title="Studio Module Rollout"
  subtitle="12 studios · 5 modules"
  data={studios}
  columns={[
    { key: "studio", header: "Studio", pin: "left", sortable: true,
      render: (v, row) => (
        <div>
          <span className="font-medium">{v}</span>
          <span className="ml-2 text-[10px] text-[var(--muted)]">{row.tier}</span>
        </div>
      ),
    },
    { key: "am", header: "Acct Mgr", sortable: true },
    { key: "rooms", header: "Rooms", type: "number", sortable: true, width: 70 },
    ...modules.map((m) => ({
      key: m.toLowerCase(), header: m, align: "center", width: 90,
      render: renderStage,
    })),
  ]}
  footnote={
    <div className="flex flex-wrap gap-x-5 gap-y-1">
      <span className="flex items-center gap-1"><CheckCircle2 className="h-3 w-3 text-emerald-500" /> Live</span>
      <span className="flex items-center gap-1"><Loader className="h-3 w-3 text-amber-500" /> In progress</span>
      <span className="flex items-center gap-1"><Wrench className="h-3 w-3 text-blue-500" /> Setting up</span>
      <span className="flex items-center gap-1"><Minus className="h-3 w-3 opacity-30" /> Not enabled</span>
    </div>
  }
  renderExpanded={(row) => ( /* customer detail grid */ )}
  searchable
  dense
  maxRows={10}
/>

The Everything Table

One table that is the dashboard. Every column type in play — sparklines, progress bars, status dots, badges, bar fills, conditional colors — plus expandable detail rows with nested KpiCards. Try doing this in Metabase.

Account health matrix

Account Health Matrix

6 accounts · click any row to expand

AccountARR7d TrendHealthAdoptionNPSChurn RiskRenews
NovaTechEnterprise
284K
healthy
72
0%
45d
BrightPathMid-Market
126K
healthy
61
0%
120d
Meridian LabsMid-Market
89K
at-risk
28
0%
30d
Apex IndustriesEnterprise
412K
healthy
84
0%
200d
Flux DigitalSMB
34K
churning
12
1%
14d
Orion GroupEnterprise
198K
healthy
55
0%
90d
<DataTable
  title="Account Health Matrix"
  subtitle="6 accounts · click any row to expand"
  data={accounts}
  columns={[
    { key: "account", header: "Account", pin: "left", sortable: true },
    { key: "arr", header: "ARR", type: "currency", format: "compact", sortable: true },
    { key: "trend", header: "7d Trend", type: "sparkline" },
    { key: "health", header: "Health", type: "badge" },
    { key: "adoption", header: "Adoption", type: "progress" },
    { key: "nps", header: "NPS", type: "number", sortable: true,
      conditions: [
        { min: 50, color: "emerald" },
        { min: 20, color: "amber" },
        { color: "red" },
      ],
    },
    { key: "risk", header: "Churn Risk", type: "bar", format: "percent" },
    { key: "renewsIn", header: "Renews", type: "number", sortable: true,
      render: (v) => <Badge variant={v <= 30 ? "danger" : v <= 90 ? "warning" : "default"} size="sm">{v}d</Badge>,
    },
  ]}
  renderExpanded={(row) => (
    <MetricGrid columns={4}>
      <KpiCard title="Seats Used" value={`${row.seatsUsed}/${row.seats}`} ... />
      <KpiCard title="ARR" value={row.arr} format="currency" ... />
      <KpiCard title="Adoption" value={row.adoption} format="percent" ... />
      <KpiCard title="NPS Score" value={row.nps} ... />
    </MetricGrid>
  )}
  searchable
  dense
/>

Ops War Room

A live operations board that feels alive. Pulsing status indicators for degraded services, data-driven callouts that auto-switch severity, KPIs with conditional glow — all updating from a single data source. BI dashboards are screenshots by comparison.

System status board

System Status

Live

Real-time infrastructure health

API Gateway

Healthy

↑ Risingfor 3d
Database Primary

Degraded

↓ Fallingfor 2h
CDN Edge

Healthy

for 7d
Queue Workers

Down

↓ Fallingfor 30m
P50 Latency

ms

0
+10.5%
Error Rate
0%
+166.7%
Active Connections
0
+14.7%
Queue Depth
0
+660.0%
Request Volume (24h)
<MetricProvider theme="slate">
  <MetricGrid>
    <SectionHeader title="System Status" subtitle="Real-time infrastructure health" />
    <StatusIndicator size="card" title="API Gateway" value={99.97} rules={[...]} since={...} trend={[...]} />
    <StatusIndicator size="card" title="Database Primary" value={98.2} rules={[...]} />
    <StatusIndicator size="card" title="CDN Edge" value={99.99} rules={[...]} />
    <StatusIndicator size="card" title="Queue Workers" value={94.1} rules={[...]} />
    <Callout value={94.1} rules={[
      { min: 99, variant: "success", title: "All systems operational" },
      { min: 95, variant: "warning", title: "Degraded: {value}% uptime" },
      { variant: "error", title: "Critical: {value}% uptime" },
    ]} />
    <KpiCard title="P50 Latency" value={42} ... conditions={[...]} />
    <KpiCard title="Error Rate" value={0.08} format="percent" ... conditions={[...]} />
    <KpiCard title="Active Connections" value={12847} ... />
    <KpiCard title="Queue Depth" value={342} ... conditions={[...]} />
    <AreaChart title="Request Volume (24h)" data={...} thresholds={[...]} referenceLines={[...]} />
  </MetricGrid>
</MetricProvider>

The Metric Sandwich

Three layers, one story. KPI cards on top for the headlines, a chart in the middle for the trend, a detail table on the bottom for the drill-down — all wired together with linked hover and cross-filtering. Hover a point on the chart and the matching table row highlights. Click a table row and everything filters. Impossible in any BI tool.

Revenue deep-dive

Total Revenue
$0
+17.5%
Avg Deal Size
$0
+16.7%
Active Products

across 525 customers

0
+33.3%
Net Margin
0%
Target: 40%95%
Monthly Revenue vs Expenses
Product Breakdown

Revenue by product line

ProductRevenue6m TrendCustomersGrowthStatus
Pro Plan$38.2K
1420%active
Enterprise$24.8K
280%active
Starter$5.2K
310-0%warning
API Add-on$2.8K
451%active
<LinkedHoverProvider>
  <CrossFilterProvider>
    <MetricGrid>
      {/* Layer 1: KPI headlines */}
      <KpiCard title="Total Revenue" value={329000} format="currency" comparison={...} sparkline={...} />
      <KpiCard title="Avg Deal Size" value={245} format="currency" comparison={...} />
      <KpiCard title="Active Products" value={4} comparison={...} />
      <KpiCard title="Net Margin" value={0.38} format="percent" goal={...} conditions={...} />

      {/* Layer 2: Trend chart */}
      <AreaChart title="Monthly Revenue vs Expenses" data={monthly} ... referenceLines={[...]} thresholds={[...]} />

      {/* Layer 3: Detail table with everything */}
      <DataTable title="Product Breakdown" data={products} columns={[...]} renderExpanded={...} />
    </MetricGrid>
  </CrossFilterProvider>
</LinkedHoverProvider>