MetricUI
The missing UI layer for React dashboards. KPI cards, charts, tables, and layout — with built-in formatting, theming, data states, and zero config.
Homepage · Docs · SaaS Demo · GitHub Demo · MCP Server · Roadmap
Live SaaS demo — DashboardHeader, filters, KpiCards with conditions, Callout alerts, Waterfall chart. GitHub demo also available.
Install
npm install metricui
That's it. All chart dependencies are included.
Why MetricUI?
You're building a dashboard. You need KPI cards, charts, tables. You reach for Recharts + shadcn + custom CSS and spend days wiring up formatting, dark mode, loading states, and responsive layouts.
MetricUI does all of that in one import.
import {
MetricProvider, FilterProvider, DashboardHeader, PeriodSelector,
MetricGrid, KpiCard, AreaChart, DonutChart, DataTable, Callout,
} from "metricui";
import "metricui/styles.css";
export default function Dashboard() {
return (
<MetricProvider theme="emerald">
<FilterProvider defaultPreset="30d">
<DashboardHeader title="Revenue" lastUpdated={new Date()} actions={<PeriodSelector comparison />} />
<MetricGrid>
<MetricGrid.Section title="Key Metrics" />
<KpiCard title="Revenue" value={127450} format="currency"
comparison={{ value: 113500 }} comparisonLabel="vs last month"
sparkline={{ data: [89, 94, 99, 103, 109, 114, 127], previousPeriod: [78, 82, 85, 88, 91, 95, 98] }}
conditions={[
{ when: "above", value: 120000, color: "emerald" },
{ when: "below", value: 90000, color: "red" },
]}
/>
<KpiCard title="Users" value={8420} format="compact"
comparison={[{ value: 7680, label: "vs last month" }, { value: 6200, label: "vs last year" }]}
highlight
/>
<KpiCard title="Churn" value={3.2} format="percent"
comparison={{ value: 3.7, invertTrend: true }}
goal={{ value: 2.5, showTarget: true, showRemaining: true }}
/>
<Callout value={12.3} rules={[
{ min: 10, variant: "success", title: "Strong Growth", message: "Revenue grew {value}% this month." },
{ max: 0, variant: "error", title: "Revenue Declined", message: "Revenue dropped {value}%." },
]} />
<MetricGrid.Section title="Trends" border />
<AreaChart data={revenueData} comparisonData={prevPeriod} format="currency" title="Revenue Trend"
referenceLines={[{ axis: "y", value: 50000, label: "Target", style: "dashed" }]}
thresholds={[{ from: 0, to: 30000, color: "#EF4444", opacity: 0.05 }]}
/>
<DonutChart data={planBreakdown} format="currency" title="By Plan" centerValue="$127K" centerLabel="Total" />
<MetricGrid.Section title="Details" border />
<DataTable data={customers} title="Top Customers" columns={columns} searchable pageSize={10} />
</MetricGrid>
</FilterProvider>
</MetricProvider>
);
}
Zero layout code. MetricGrid auto-detects component types — KPIs row up, charts pair, tables go full width. DashboardHeader shows live status with auto-ticking "Updated Xm ago". FilterProvider + PeriodSelector wire time filtering across the page. Responsive out of the box.
Features
KPI Cards — Not Just Numbers
Sparkline with previous-period overlay. Goal progress bars. Conditional red/amber/green coloring. Multiple comparison badges. Copyable values. Drill-down links. Docs →
Charts with Reference Lines & Threshold Bands
Mark targets, benchmarks, and danger zones directly on charts. Comparison overlays show period-over-period trends as dashed lines. Works on AreaChart, LineChart, and BarChart.
<AreaChart
data={revenueData}
comparisonData={previousPeriod}
referenceLines={[{ axis: "y", value: 50000, label: "Target", style: "dashed" }]}
thresholds={[{ from: 0, to: 30000, color: "#EF4444", opacity: 0.05 }]}
format="currency"
/>
Data-Driven Alerts
Pass a value and rules — Callout auto-selects the right variant, title, and message. Supports embedded formatted metrics, action buttons, collapsible detail, and auto-dismiss.
Expandable Data Tables
Click a row and a mini-dashboard slides open — sparklines, gauges, status indicators, badges. Plus search, multi-sort, pagination, pinned columns, 12 column types, and row conditions. Docs →
Conversion Funnels
Auto-computed stage-to-stage conversion rates. Vertical or horizontal. Smooth or linear interpolation. Docs →
Light & Dark Mode
Wikipedia live demo in light mode.
CSS variables. Zero config. Every component adapts automatically. Theming guide →
Smart Format Engine
One prop formats any value. Currency, percentages, durations, compact notation — with locale support. Docs →
<KpiCard value={127450} format="currency" /> // → $127.5K
<KpiCard value={4.2} format="percent" /> // → 4.2%
<KpiCard value={3725} format="duration" /> // → 1h 2m 5s
<KpiCard value={2400000} format="number" /> // → 2.4M
Theme Presets
One prop. Entire dashboard changes color. 8 built-in presets. Custom presets via ThemePreset type. Theming guide →
<MetricProvider theme="emerald"> // Green accent + green-first chart palette
<MetricProvider theme="rose"> // Pink accent + pink-first chart palette
<MetricProvider theme="amber"> // Warm amber everything
MetricGrid — Zero Layout Code
Drop components in. It figures out the layout. Docs →
<MetricGrid>
<MetricGrid.Section title="Overview" />
<KpiCard ... /> <KpiCard ... /> <KpiCard ... />
<AreaChart ... /> {/* Auto 2/3 width */}
<DonutChart ... /> {/* Auto 1/3 width */}
<DataTable ... /> {/* Auto full width */}
</MetricGrid>
Filter System
Complete filter context — wire PeriodSelector, DropdownFilter, SegmentToggle, and FilterTags together with zero boilerplate. Filtering guide →
<FilterProvider defaultPreset="30d">
<DashboardHeader title="Dashboard" actions={<PeriodSelector comparison />} />
<SegmentToggle options={["All", "Enterprise", "SMB"]} field="segment" />
<DropdownFilter label="Region" options={regions} field="region" multiple />
<FilterTags /> {/* Auto-renders active filters as dismissible chips */}
</FilterProvider>
Data States
Every component handles loading, empty, error, and stale states out of the box. Docs →
Error Boundaries
One chart crashes? The rest keep running. Dev mode shows component name + actionable hints. Prod mode shows clean retry UI.
Accessibility
prefers-reduced-motion, focus-visible rings, ARIA attributes on charts, keyboard-accessible drill-downs. Docs →
Components
Cards & Metrics
| Component | What it does | Docs |
|---|---|---|
| KpiCard | Comparison badges, sparkline overlays, goal progress, conditional coloring, copyable, drill-down | Docs |
| StatGroup | Multiple metrics in a dense responsive grid row with per-stat comparisons | Docs |
Charts
| Component | What it does | Docs |
|---|---|---|
| AreaChart | Gradient fills, stacking, dual Y-axis, comparison overlays, reference lines, threshold bands | Docs |
| LineChart | Clean lines — same props as AreaChart without fill | Docs |
| BarChart | 6 presets, comparison/target bars, sorting, negative values, reference lines | Docs |
| BarLineChart | Dual-axis combo: bars + lines — unified data format | Docs |
| DonutChart | Center KPI content, arc labels, percentage mode | Docs |
| Gauge | Arc gauge with threshold zones, target markers, comparison badges | Docs |
| HeatMap | 2D matrix, cross-hair hover, sequential + diverging color scales | Docs |
| Funnel | Conversion pipeline with auto-computed rates | Docs |
| Waterfall | Sequential +/- changes with auto running totals, connectors | Docs |
| BulletChart | Actual vs target with qualitative range bands | Docs |
| Sparkline | Inline micro-chart with reference lines, bands, trend coloring | Docs |
Data
| Component | What it does | Docs |
|---|---|---|
| DataTable | Sort, search, pagination, expandable rows, 12 column types, pinned columns, row conditions | Docs |
Layout
| Component | What it does | Docs |
|---|---|---|
| DashboardHeader | Live/stale status, auto-ticking "Updated Xm ago", breadcrumbs, action slots | Docs |
| MetricGrid | Smart auto-layout grid with staggered reveal animations | Docs |
| SectionHeader | Labeled divider with description popover, badge, action slot | Docs |
| Divider | Horizontal/vertical separator with label, icon, accent | Docs |
Filters
| Component | What it does | Docs |
|---|---|---|
| FilterProvider | Context that wires all filter components. useMetricFilters() hook |
Guide |
| PeriodSelector | Date range presets, custom ranges, comparison toggle | Docs |
| DropdownFilter | Multi-select dimension filter with search, grouped options | Docs |
| SegmentToggle | Pill toggle with icons, badges, multi-select | Docs |
| FilterTags | Auto-renders active filters as dismissible chips | Docs |
Status & Alerts
| Component | What it does | Docs |
|---|---|---|
| Callout | Data-driven alerts with rules, {value} templates, embedded metrics |
Docs |
| StatusIndicator | Rule-based health with 5 sizes, pulse animation, trend arrows | Docs |
| Badge | 6 variants, 3 sizes, custom colors, icons, dismiss | Docs |
AI-First: MCP Server + llms.txt
Most component libraries give you docs and hope the AI figures it out. MetricUI gives the AI structured knowledge of every component, every prop, and every pattern — so it generates production-quality dashboards on the first try.
# One command. That's it.
claude mcp add --transport stdio metricui -- npx -y @metricui/mcp-server
The difference is real
Here's what happens when you prompt an AI with "Build me a SaaS dashboard with MRR, churn, users, and revenue breakdown":
Without MetricUI MCP — what the AI generates with Recharts / generic components// The AI guesses at library APIs, builds everything from scratch
import { LineChart, Line, XAxis, YAxis, PieChart, Pie } from "recharts";
export default function Dashboard() {
return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-6">SaaS Dashboard</h1>
<div className="grid grid-cols-4 gap-4 mb-8">
<div className="border rounded-lg p-4">
<p className="text-sm text-gray-500">MRR</p>
<p className="text-2xl font-bold">${(127450).toLocaleString()}</p>
<p className="text-sm text-green-600">+12.3%</p>
</div>
<div className="border rounded-lg p-4">
<p className="text-sm text-gray-500">Churn Rate</p>
<p className="text-2xl font-bold">3.2%</p>
<p className="text-sm text-green-600">-13.5%</p>
</div>
{/* ...more hand-built cards with no formatting engine, no sparklines,
no conditional coloring, no goal tracking... */}
</div>
<LineChart width={800} height={300} data={data}>
<XAxis dataKey="month" />
<YAxis />
<Line dataKey="revenue" stroke="#8884d8" />
{/* No reference lines. No threshold bands. No comparison overlay.
No dark mode. No loading states. No error boundaries. */}
</LineChart>
</div>
);
}
Raw div cards. Manual number formatting. No sparklines, no conditions, no goals, no filters, no dark mode, no data states, no error boundaries. Every dashboard looks the same.
import {
MetricProvider, FilterProvider, DashboardHeader, PeriodSelector,
MetricGrid, KpiCard, Callout, AreaChart, DonutChart, BarChart,
DataTable, Badge,
} from "metricui";
import "metricui/styles.css";
export default function Dashboard() {
return (
<MetricProvider theme="emerald">
<FilterProvider defaultPreset="30d">
<DashboardHeader
title="SaaS Metrics"
subtitle="Real-time metrics overview"
lastUpdated={new Date()}
actions={<PeriodSelector comparison />}
/>
<MetricGrid>
<MetricGrid.Section title="Key Metrics" />
<KpiCard title="Monthly Revenue" value={127450} format="currency"
comparison={{ value: 113500 }} comparisonLabel="vs last month"
sparkline={{ data: [89, 94, 99, 103, 109, 114, 127],
previousPeriod: [78, 82, 85, 88, 91, 95, 98], interactive: true }}
conditions={[
{ when: "above", value: 115000, color: "emerald" },
{ when: "below", value: 90000, color: "red" },
]} />
<KpiCard title="Churn Rate" value={3.2} format="percent"
comparison={{ value: 3.7, invertTrend: true }}
goal={{ value: 2.5, showTarget: true, showRemaining: true }}
conditions={[
{ when: "below", value: 2.5, color: "emerald" },
{ when: "above", value: 4, color: "red" },
]} />
<KpiCard title="Active Users" value={8420} format="number"
comparison={{ value: 7680 }} copyable
drillDown={{ label: "View breakdown", onClick: () => {} }} />
<KpiCard title="Conversion" value={4.8} format="percent"
comparison={{ value: 4.2 }}
sparkline={{ data: [3.1, 3.4, 3.8, 4.0, 4.1, 4.2, 4.8] }} />
<Callout value={12.3} rules={[
{ min: 15, variant: "success", title: "Exceptional Growth",
message: "Revenue grew {value}% — exceeding target by 50%." },
{ min: 5, max: 15, variant: "info", title: "Healthy Growth",
message: "Revenue grew {value}% month-over-month." },
{ max: 0, variant: "error", title: "Revenue Declined",
message: "Revenue dropped {value}%." },
]} action={{ label: "View growth report", onClick: () => {} }} />
<MetricGrid.Section title="Trends" subtitle="Last 30 days" border />
<AreaChart data={revenueData} comparisonData={comparisonData}
format="currency" title="Revenue Trend"
referenceLines={[{ axis: "y", value: 50000, label: "Target",
color: "#10B981", style: "dashed" }]}
thresholds={[{ from: 0, to: 40000, color: "#EF4444", opacity: 0.05 }]}
xAxisLabel="Month" yAxisLabel="Revenue ($)"
height={360} legend />
<DonutChart data={breakdownData} format="currency"
title="Revenue by Plan" centerValue="$99.4K" centerLabel="Total MRR" />
<MetricGrid.Item span="full">
<BarChart preset="grouped" data={channelData}
keys={["revenue", "conversions"]} indexBy="channel"
sort="desc" format="currency" title="Channel Performance" legend />
</MetricGrid.Item>
<MetricGrid.Section title="Details" border />
<DataTable data={tableData} title="Top Customers" searchable
columns={[
{ key: "name", header: "Customer", sortable: true },
{ key: "plan", header: "Plan",
render: (v) => <Badge variant={v === "Enterprise" ? "info" : "default"}>{String(v)}</Badge> },
{ key: "mrr", header: "MRR", format: "currency", sortable: true },
{ key: "status", header: "Status",
render: (v) => <Badge variant={v === "active" ? "success" : v === "at-risk" ? "warning" : "danger"}>{String(v)}</Badge> },
]} />
</MetricGrid>
</FilterProvider>
</MetricProvider>
);
}
Same prompt. The AI with MetricUI MCP generates:
- DashboardHeader with live status + auto-ticking "Updated Xm ago"
- FilterProvider + PeriodSelector with comparison toggle
- MetricGrid auto-layout (zero CSS)
- KpiCards with conditional coloring, goal progress bars, sparkline overlays, drill-down links
- AreaChart with dashed target reference line, danger zone threshold band, and previous-period comparison overlay
- Data-driven Callout that auto-picks severity from a growth number
- Sorted grouped BarChart, DonutChart with center KPI
- Searchable DataTable with Badge status columns
- Theme preset, dark mode, animations, error boundaries — all automatic
The AI doesn't guess. It knows. 13 tools, 26 components, every prop, every pattern. Full MCP Server docs →
llms.txt
Machine-readable documentation for AI models at /llms.txt. Every component, every prop, every type, every pattern — so even AI tools without MCP support can generate correct MetricUI code.
Roadmap
MetricUI is building toward 1.0 — cross-filtering, zero-config charts, anomaly detection, data stories, and a stable API you can build on with confidence. See what's shipped, what's next, and where we're headed:
Built With
- Nivo — chart rendering (line, bar, pie, heatmap)
- Tailwind CSS — styling (ships pre-built, Tailwind not required in your project)
- Lucide — icons
- React 18/19
License
MIT
Built for developers who care about design.