awesome-kpi-card
v0.1.0
Published
Enterprise-grade KPI Card component for React.
Maintainers
Keywords
Readme
awesome-kpi-card
Enterprise-grade KPI Card component for React. Three layout variants (compact, detailed, premium), sparkline, trend arrow, variance badge, RAG status indicator, optional REST data fetching with auto-refresh, and full theming via CSS custom properties — all styles injected at runtime so no CSS import is needed.
Features
- 3 variants —
compact(tight, label + value + variance),detailed(sparkline + variance line),premium(sparkline + target + previous + all indicators) - Trend arrow — ▲ / ▼ / — based on
valuevsprevious, with a configurable dead-zone (trendSensitivity) - Variance badge —
+4.50%/−3.20%coloured by direction, shown in header (compact) or value row (detailed/premium) - RAG status — colour-coded dot + label driven by
value / targetratio against configurablethresholds - SVG sparkline — polyline chart from
historyarray, with optional animated draw - REST data fetching —
endpointprop +autoRefreshpolling; localdataprop always takes precedence transformhook — normalise any remote payload shape before renderingonClickcallback — receives the fully resolved data object- 4 built-in themes —
dark,light,purpleplus the default — all driven by CSS custom properties - Self-contained — styles injected at runtime; no CSS import, no sub-folder dependencies
- ESM + CJS dual build — works in Vite, Next.js, CRA, and any modern bundler
- Zero runtime dependencies beyond React
Installation
npm install awesome-kpi-card
# or
yarn add awesome-kpi-card
# or
pnpm add awesome-kpi-cardPeer dependencies — React ≥ 17 and ReactDOM ≥ 17 must already be installed.
Quick Start
No CSS import needed — styles are injected automatically on first render.
import AwesomeKpiCard from 'awesome-kpi-card';
export default function App() {
return (
<AwesomeKpiCard
variant="premium"
data={{
label: 'Revenue',
value: 5200000,
target: 5000000,
previous: 4800000,
history: [4000000, 4200000, 4500000, 4800000, 5200000],
}}
format="currency"
currency="USD"
thresholds={{ good: 1, warning: 0.85 }}
animated
/>
);
}Both import styles work:
// Default import
import AwesomeKpiCard from 'awesome-kpi-card';Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | 'compact' \| 'detailed' \| 'premium' | 'detailed' | Controls layout, density, and which elements are shown by default. |
| label | string | 'KPI Metric' | KPI title label shown in the card header. |
| value | number | 0 | Current KPI value. |
| target | number | — | Target value. Used for variance, RAG status, and the target line in premium. |
| previous | number | — | Previous period value. Used for the trend arrow direction. |
| history | number[] | — | Array of historical values for the sparkline (oldest → newest). |
| data | object | — | Local data object with any of { label, value, target, previous, history }. Takes precedence over endpoint. |
| endpoint | string | — | REST URL to fetch KPI data from. Response root or .data key is used. |
| method | string | 'GET' | HTTP method for the endpoint request. |
| autoRefresh | number | — | Polling interval in ms. Requires endpoint. |
| transform | (normalized, raw) => object | — | Transforms the remote payload before it is merged into the card's data. |
| thresholds | { good: number, warning: number } | { good: 1, warning: 0.85 } | RAG ratio thresholds. good = value/target ≥ good, warning = ≥ warning. |
| format | 'number' \| 'currency' \| 'percent' | 'number' | Value display format. |
| currency | string | 'USD' | ISO currency code for format="currency". |
| showSparkline | boolean | variant default | Override sparkline visibility. |
| showVariance | boolean | variant default | Override variance badge visibility. |
| showTrend | boolean | true | Show/hide the trend arrow. |
| showTarget | boolean | variant default | Show the target meta line (on by default in premium). |
| showPrevious | boolean | variant default | Show the previous meta line (on by default in premium). |
| animated | boolean | false | Animate the sparkline draw on mount. |
| trendSensitivity | number | 0 | Dead-zone delta — differences ≤ this value show neutral — instead of ▲/▼. |
| statusLabels | { good?: string, warning?: string, critical?: string } | — | Override the default RAG status label strings. |
| onClick | (data) => void | — | Fired when the card is clicked. Receives the fully resolved data object. |
| className | string | — | Additional CSS class on the root <section>. |
| style | object | — | Inline styles on the root element. |
Variant defaults
| Feature | compact | detailed | premium |
|---|---|---|---|
| showSparkline | false | true | true |
| showVariance | true | true | true |
| showTarget | false | false | true |
| showPrevious | false | false | true |
Examples
Example 1 — Three variants side by side
import AwesomeKpiCard from 'awesome-kpi-card';
var kpiData = {
label: 'Monthly Revenue',
value: 5200000,
target: 5000000,
previous: 4800000,
history: [3800000, 4100000, 4400000, 4700000, 4900000, 5200000],
};
var thresholds = { good: 1, warning: 0.9 };
export default function VariantsDemo() {
return (
<div style={{ display: 'flex', gap: 20, padding: 32, background: '#10151c', flexWrap: 'wrap' }}>
<div style={{ width: 260 }}>
<AwesomeKpiCard
variant="compact"
data={kpiData}
format="currency"
thresholds={thresholds}
/>
</div>
<div style={{ width: 300 }}>
<AwesomeKpiCard
variant="detailed"
data={kpiData}
format="currency"
thresholds={thresholds}
animated
/>
</div>
<div style={{ width: 340 }}>
<AwesomeKpiCard
variant="premium"
data={kpiData}
format="currency"
thresholds={thresholds}
animated
/>
</div>
</div>
);
}Example 2 — Live REST API with auto-refresh and custom status labels
import AwesomeKpiCard from 'awesome-kpi-card';
export default function LiveKpi() {
return (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 280px)', gap: 20, padding: 32, background: '#0a0f16' }}>
<AwesomeKpiCard
variant="premium"
endpoint="/api/kpi/revenue"
autoRefresh={30000}
format="currency"
currency="USD"
thresholds={{ good: 1, warning: 0.9 }}
statusLabels={{ good: 'Exceeding', warning: 'Near Miss', critical: 'Behind' }}
animated
/>
<AwesomeKpiCard
variant="detailed"
endpoint="/api/kpi/nps"
autoRefresh={60000}
format="number"
thresholds={{ good: 0.9, warning: 0.75 }}
statusLabels={{ good: 'Excellent', warning: 'Acceptable', critical: 'Poor' }}
/>
<AwesomeKpiCard
variant="detailed"
endpoint="/api/kpi/uptime"
autoRefresh={10000}
format="percent"
thresholds={{ good: 0.999, warning: 0.995 }}
statusLabels={{ good: 'Healthy', warning: 'Degraded', critical: 'Down' }}
/>
</div>
);
}Example 3 — onClick, transform, and a custom side panel
Demonstrates transforming a non-standard API response and handling card clicks with a detail drawer.
import { useState } from 'react';
import AwesomeKpiCard from 'awesome-kpi-card';
// The API returns: { metric: "...", current: 123, goal: 100, trend: [...] }
// We use `transform` to map it to the expected shape.
function transformApiResponse(normalized) {
return {
label: normalized.metric,
value: normalized.current,
target: normalized.goal,
history: normalized.trend,
};
}
var kpis = [
{ id: 'cac', label: 'Customer Acq. Cost', value: 84, target: 90, previous: 91, history: [100, 96, 93, 91, 88, 84], format: 'currency' },
{ id: 'churn', label: 'Monthly Churn', value: 0.021, target: 0.025, previous: 0.028, history: [0.04, 0.035, 0.03, 0.028, 0.025, 0.021], format: 'percent' },
{ id: 'arpu', label: 'ARPU', value: 142, target: 130, previous: 128, history: [110, 118, 124, 128, 135, 142], format: 'currency' },
{ id: 'activation', label: 'Activation Rate', value: 0.712, target: 0.70, previous: 0.68, history: [0.61, 0.64, 0.67, 0.68, 0.70, 0.712], format: 'percent' },
];
var drawerStyle = {
position: 'fixed', top: 0, right: 0, width: 340,
height: '100vh', background: '#111827',
borderLeft: '1px solid rgba(255,255,255,0.08)',
padding: 32, zIndex: 100, color: '#f7f3e8',
fontFamily: 'sans-serif', overflowY: 'auto',
display: 'flex', flexDirection: 'column', gap: 16,
};
export default function KpiDashboard() {
var [selected, setSelected] = useState(null);
return (
<div style={{ padding: 32, background: '#0a0f16', minHeight: '100vh' }}>
<h2 style={{ color: '#f7f3e8', fontFamily: 'sans-serif', marginBottom: 24 }}>Growth Metrics</h2>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(260px,1fr))', gap: 20 }}>
{kpis.map(function(kpi) {
return (
<AwesomeKpiCard
key={kpi.id}
variant="detailed"
label={kpi.label}
value={kpi.value}
target={kpi.target}
previous={kpi.previous}
history={kpi.history}
format={kpi.format}
thresholds={{ good: 1, warning: 0.9 }}
animated
onClick={function(data) { setSelected({ ...kpi, resolved: data }); }}
/>
);
})}
</div>
{selected && (
<div style={drawerStyle}>
<button
onClick={function() { setSelected(null); }}
style={{ alignSelf: 'flex-end', background: 'transparent', border: '1px solid rgba(255,255,255,0.15)', color: '#fff', borderRadius: 4, width: 28, height: 28, cursor: 'pointer' }}
>✕</button>
<div style={{ fontSize: 22, fontWeight: 700 }}>{selected.label}</div>
<div style={{ fontSize: 13, color: 'rgba(247,243,232,0.6)' }}>Format: {selected.format}</div>
<div style={{ height: 1, background: 'rgba(255,255,255,0.08)' }} />
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, fontSize: 13 }}>
<div>Current: <strong>{String(selected.value)}</strong></div>
<div>Target: <strong>{String(selected.target)}</strong></div>
<div>Previous: <strong>{String(selected.previous)}</strong></div>
</div>
<div style={{ marginTop: 'auto', fontSize: 11, color: 'rgba(255,255,255,0.2)', fontFamily: 'monospace' }}>
ID: {selected.id}
</div>
</div>
)}
</div>
);
}Theming
All colours are CSS custom properties. Apply a theme class to a wrapper element:
<div class="theme-dark"> <!-- or theme-light / theme-purple -->
...
</div>Or override the variables directly:
:root {
--kpi-bg: #0a0f16;
--kpi-text: #f7f3e8;
--kpi-positive: #4cd39b; /* RAG good / positive variance */
--kpi-negative: #ff6a6a; /* RAG critical / negative variance */
--kpi-warning: #f1b463; /* RAG warning */
--kpi-border: rgba(255,255,255,0.08);
--kpi-muted: rgba(247,243,232,0.7);
--kpi-panel: rgba(13,18,26,0.92);
}The --kpi-accent variable is set automatically per-card to the current RAG colour and drives the card glow.
Built-in theme classes
| Class | Background | Accent |
|---|---|---|
| (default / dark) | #10151c | #4cd39b / #ff6a6a / #f1b463 |
| .theme-light | #f6f6f2 | #1a9b64 / #e15656 / #cf8a3a |
| .theme-dark | #0a0f16 | #4cd39b / #ff6a6a / #f1b463 |
| .theme-purple | #1a0d2e | #5ee6b8 / #ff7aa2 / #f2c264 |
License
MIT
