@mshafiqyajid/react-chart
Pure SVG chart suite — BarChart, LineChart, PieChart, AreaChart, ScatterChart, GaugeChart. Crosshair tooltips, gradient fills, animated path reveal, interactive legend, reference lines + annotations, imperative SVG/PNG export, niceDomain ticks, responsive resize, named palettes, loading / empty / error states. Zero external dependencies.
Playground #
import { BarChart } from "@mshafiqyajid/react-chart/styled";
import "@mshafiqyajid/react-chart/styles.css";
<BarChart data={data} height={240} domain="nice" />Install #
npm install @mshafiqyajid/react-chart Quick start #
import { BarChart, LineChart, PieChart, AreaChart, ScatterChart, GaugeChart } from "@mshafiqyajid/react-chart/styled";
import "@mshafiqyajid/react-chart/styles.css";
const data = [
{ label: "Jan", value: 42 },
{ label: "Feb", value: 68 },
{ label: "Mar", value: 55 },
];
<BarChart data={data} height={240} showValues animated />
<LineChart data={data} height={240} showDots showGrid animated />
<AreaChart data={data} height={240} smooth fill={{ from: "#6366f1", to: "#6366f1" }} />
<PieChart data={pieData} showLegend animated />
<ScatterChart data={[{ x: 1, y: 4, size: 10 }, { x: 2, y: 7, size: 22 }]} />
<GaugeChart value={72} thresholds={[{ from: 0, color: "#dc2626" }, { from: 50, color: "#eab308" }, { from: 80, color: "#16a34a" }]} /> AreaChart #
Filled-area variant with crosshair tooltip, gradient fills, stacked mode, interactive legend, and reference / annotation lines.
<AreaChart
data={[
{ label: "Jan", series: [
{ name: "Sales", values: [120] },
{ name: "Refunds", values: [12] },
]},
{ label: "Feb", series: [
{ name: "Sales", values: [180] },
{ name: "Refunds", values: [8] },
]},
{ label: "Mar", series: [
{ name: "Sales", values: [240] },
{ name: "Refunds", values: [15] },
]},
]}
height={260}
smooth
stacked
fill={{ from: "#6366f1", to: "#6366f1", fromOpacity: 0.5, toOpacity: 0 }}
referenceLines={[{ value: 200, label: "target", color: "#dc2626", dashed: true }]}
annotations={[{ x: "Feb", label: "launch", color: "#16a34a" }]}
showLegend
showTooltip // crosshair + multi-series tooltip
/> ScatterChart #
Points only, with optional size dimension for bubble charts. Hover any point for a tooltip with x / y / size.
<ScatterChart
series={[
{ name: "Cohort A", points: [
{ x: 0.4, y: 12, size: 4 },
{ x: 0.7, y: 28, size: 18 },
]},
{ name: "Cohort B", points: [
{ x: 0.5, y: 22, size: 8 },
{ x: 0.9, y: 35, size: 26 },
]},
]}
bubbleRange={[4, 28]}
showLegend
/> GaugeChart #
Single-value KPI dial. Thresholds drive both the colour and an optional centered label.
<GaugeChart
value={72}
min={0}
max={100}
sweep={220} // 180 = semicircle, 360 = ring
thresholds={[
{ from: 0, color: "#dc2626", label: "Critical" },
{ from: 50, color: "#eab308", label: "Warning" },
{ from: 80, color: "#16a34a", label: "Healthy" },
]}
formatValue={(v) => `${v}%`}
label="CPU"
/> Visual variants #
Each chart accepts a variant prop for design alternatives — not just colours, but different visualisation styles.
| Chart | Variants | Notes |
|---|---|---|
| BarChart | "default" | "rounded" | "lollipop" | "lollipop" renders as thin stem + circle marker. direction="horizontal" still works. |
| LineChart | "default" | "sparkline" | "stepped" | "dashed" | "stepped" draws stair-step segments; "dashed" applies stroke-dasharray. |
| AreaChart | "default" | "stepped" | "stepped" renders the fill as level plateaus. |
| PieChart | "default" | "donut" | "semi" | "semi" renders a horizontal half-donut (gauge-style); implies donut. |
| ScatterChart | "points" | "connected" | "connected" draws a 1.5 px line through points in array order. |
| GaugeChart | "arc" | "ring" | "linear" | "ring" forces sweep to 360°; "linear" renders a horizontal bar with markers. |
Interactive features (AreaChart) #
The 1.0 release adds a stack of interactivity to AreaChart that goes well beyond what most chart libraries ship by default:
- Click-to-drill —
onPointClick(payload)fires with every visible series' value at the clicked x. - Pinned tooltip — click any point to pin the tooltip; click again to unpin.
tooltipPindefaulttrue. - Brush range selection — drag horizontally to select;
onRangeSelect({ startLabel, endLabel, startIndex, endIndex })fires once on release. - Hover-dim siblings — hovering a legend item fades the rest to
0.22opacity. Toggle withhoverDim. - Synchronised cursor — wrap a group of charts in
<ChartSyncProvider>and pass the samesyncIdto share crosshair x-index. Dashboard-friendly. - Keyboard navigation — focus the chart and use
←/→/Home/Endto move the crosshair,Enter/Spaceto fireonPointClick,Escto clear.
import { ChartSyncProvider, AreaChart } from "@mshafiqyajid/react-chart/styled";
<ChartSyncProvider>
<AreaChart
data={revenue}
syncId="dashboard-q1"
onPointClick={(rows) => openDrawer(rows)}
onRangeSelect={(range) => setRange(range)}
/>
<AreaChart data={users} syncId="dashboard-q1" />
{/* Both charts now share the same crosshair x-index. */}
</ChartSyncProvider> Imperative export #
import { useRef } from "react";
import { AreaChart, type AreaChartHandle } from "@mshafiqyajid/react-chart/styled";
const ref = useRef<AreaChartHandle>(null);
<>
<button onClick={async () => {
const png = await ref.current?.exportPNG();
if (png) downloadBlob(png, "chart.png");
}}>Download PNG</button>
<AreaChart ref={ref} data={data} />
</> AreaChart, ScatterChart, and GaugeChart all expose an imperative ref handle with exportSVG() (returns the SVG markup string) and exportPNG() (returns a 2× Blob).
Cross-cutting features (Line / Area / Bar) #
| Feature | Prop | Notes |
|---|---|---|
| Crosshair tooltip | showTooltip | Vertical guide that snaps to the nearest x; lists all series at that point. |
| Animated reveal | animate / animated | Stroke-dasharray reveal on Line / Area; transform reveal on Bar. Respects prefers-reduced-motion. |
| Gradient fills | fill: { from, to, fromOpacity?, toOpacity? } | Vertical gradient for AreaChart fill (defaults: 1 → 0 opacity). |
| Reference lines | referenceLines: [{ value, label?, color?, dashed? }] | Horizontal targets / thresholds. |
| Annotations | annotations: [{ x, label?, color? }] | Vertical guide at a label / index (release marker, deploy, etc.). |
| Interactive legend | showLegend + hiddenSeries / onHiddenSeriesChange | Click any series swatch to toggle visibility. |
Shared props (all charts) #
Prop Type Default Description colorScheme "default" | "warm" | "cool" | "muted" | "vivid" | "mono" "default" Named palette used when colors prop is not provided formatValue (value, point, index) => ReactNode — Formats value in tooltip / value labels formatLabel (label, index) => ReactNode — Formats axis / slice labels renderTooltip (payload) => ReactNode — Custom tooltip body. Payload: { label, value, series?, color, point, index } responsive boolean false Resize to fill container via ResizeObserver aspectRatio number — width/height ratio when responsive minWidth / minHeight number 200 / 100 Lower bounds for responsive sizing loading boolean false Renders shimmer skeleton instead of the chart error boolean false Renders errorText alert instead of the chart emptyText / errorText ReactNode "No data" / "Failed to load chart" State copy
BarChart props #
Prop Type Default Description data DataPoint[] | SeriesDataPoint[] — Chart data width / height number 600 / 300 SVG dimensions (overridden when responsive) direction "vertical" | "horizontal" "vertical" Bar orientation stacked boolean false Stacked bars (multi-series) gap / radius number 4 / 3 Bar gap and corner radius showValues boolean false Show value labels on bars (uses formatValue) animated boolean false Animate on mount colors string[] — Custom bar colors (overrides colorScheme) domain [min, max] | "nice" — Y-axis domain — "nice" snaps to round numbers yTicks number | number[] 5 Tick count or explicit values xTicks string[] — Override category labels onClick (point, index) => void — Bar click handler xLabel / yLabel string — Axis labels tooltip boolean true Show hover tooltip
LineChart props #
Prop Type Default Description data DataPoint[] | SeriesDataPoint[] — Chart data width / height number 600 / 300 SVG dimensions (overridden when responsive) variant "default" | "sparkline" "default" "sparkline" drops padding, dots, labels, legend, tooltip showGrid / showDots / showLabels / showLegend boolean false / true / false / false Visibility toggles (auto-off in sparkline) smooth boolean false Curved line interpolation tone "neutral" | "primary" "neutral" Line color tone for flat data animated boolean false Animate on mount colors string[] — Per-series colors (overrides colorScheme) area boolean false Fill area under the line domain [min, max] | "nice" — Y-axis domain yTicks / xTicks number | number[] / string[] 5 / — Tick count, explicit y values, or override x labels onClick (point, index) => void — Data point click handler xLabel / yLabel string — Axis labels tooltip boolean true Show hover tooltip
PieChart props #
Prop Type Default Description data DataPoint[] — Slice data (with optional color) size number 300 SVG size in px (overridden when responsive) donut / donutWidth boolean / number false / 60 Donut + ring thickness showLabels / showLegend boolean false Show slice labels and color legend animated boolean false Animate on mount colors string[] — Per-slice colors (overrides colorScheme) hoverOffset number 0 Pixels to translate slice outward on hover selectedIndex number | null — Controlled selected slice onSelectedChange (index | null) => void — Click toggles selection. Provide to enable selection UI selectedOffset number 8 Pixels for selected slice translate onClick (slice, index) => void — Slice click handler tooltip boolean true Show hover tooltip innerLabel ReactNode — Label rendered in donut center
Headless #
import { useChart, niceDomain, getPalette } from "@mshafiqyajid/react-chart";
const { points, paths, series, scales, viewBox } = useChart({
data, // DataPoint[] or SeriesDataPoint[]
type: "line", // "line" | "bar" | "pie"
width: 600,
height: 300,
padding: 40, // optional, default 40
smooth: true, // line only
stacked: false, // bar only
donut: true, // pie — true | { innerRadius: 50 }
domain: "nice", // optional — [min, max] | "nice"
});
// series is set when data is multi-series. Each entry is { name, color?, path, points, values }.
// niceDomain(min, max, tickCount?) returns [niceMin, niceMax, ticks[]] for axis math.
// getPalette(scheme) returns the named color array.
Edit this page on GitHub