react ~16 KB 0 deps v1.0.2 ↗ GitHub ↗

@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 #

020406080100JanFebMarAprMayJun
Props
TSX
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.

ChartVariantsNotes
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-drillonPointClick(payload) fires with every visible series' value at the clicked x.
  • Pinned tooltip — click any point to pin the tooltip; click again to unpin. tooltipPin default true.
  • 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.22 opacity. Toggle with hoverDim.
  • Synchronised cursor — wrap a group of charts in <ChartSyncProvider> and pass the same syncId to share crosshair x-index. Dashboard-friendly.
  • Keyboard navigation — focus the chart and use //Home/End to move the crosshair, Enter/Space to fire onPointClick, Esc to 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) #

FeaturePropNotes
Crosshair tooltipshowTooltipVertical guide that snaps to the nearest x; lists all series at that point.
Animated revealanimate / animatedStroke-dasharray reveal on Line / Area; transform reveal on Bar. Respects prefers-reduced-motion.
Gradient fillsfill: { from, to, fromOpacity?, toOpacity? }Vertical gradient for AreaChart fill (defaults: 1 → 0 opacity).
Reference linesreferenceLines: [{ value, label?, color?, dashed? }]Horizontal targets / thresholds.
Annotationsannotations: [{ x, label?, color? }]Vertical guide at a label / index (release marker, deploy, etc.).
Interactive legendshowLegend + hiddenSeries / onHiddenSeriesChangeClick any series swatch to toggle visibility.

Shared props (all charts) #

PropTypeDefaultDescription
colorScheme"default" | "warm" | "cool" | "muted" | "vivid" | "mono""default"Named palette used when colors prop is not provided
formatValue(value, point, index) => ReactNodeFormats value in tooltip / value labels
formatLabel(label, index) => ReactNodeFormats axis / slice labels
renderTooltip(payload) => ReactNodeCustom tooltip body. Payload: { label, value, series?, color, point, index }
responsivebooleanfalseResize to fill container via ResizeObserver
aspectRationumberwidth/height ratio when responsive
minWidth / minHeightnumber200 / 100Lower bounds for responsive sizing
loadingbooleanfalseRenders shimmer skeleton instead of the chart
errorbooleanfalseRenders errorText alert instead of the chart
emptyText / errorTextReactNode"No data" / "Failed to load chart"State copy

BarChart props #

PropTypeDefaultDescription
dataDataPoint[] | SeriesDataPoint[]Chart data
width / heightnumber600 / 300SVG dimensions (overridden when responsive)
direction"vertical" | "horizontal""vertical"Bar orientation
stackedbooleanfalseStacked bars (multi-series)
gap / radiusnumber4 / 3Bar gap and corner radius
showValuesbooleanfalseShow value labels on bars (uses formatValue)
animatedbooleanfalseAnimate on mount
colorsstring[]Custom bar colors (overrides colorScheme)
domain[min, max] | "nice"Y-axis domain — "nice" snaps to round numbers
yTicksnumber | number[]5Tick count or explicit values
xTicksstring[]Override category labels
onClick(point, index) => voidBar click handler
xLabel / yLabelstringAxis labels
tooltipbooleantrueShow hover tooltip

LineChart props #

PropTypeDefaultDescription
dataDataPoint[] | SeriesDataPoint[]Chart data
width / heightnumber600 / 300SVG dimensions (overridden when responsive)
variant"default" | "sparkline""default""sparkline" drops padding, dots, labels, legend, tooltip
showGrid / showDots / showLabels / showLegendbooleanfalse / true / false / falseVisibility toggles (auto-off in sparkline)
smoothbooleanfalseCurved line interpolation
tone"neutral" | "primary""neutral"Line color tone for flat data
animatedbooleanfalseAnimate on mount
colorsstring[]Per-series colors (overrides colorScheme)
areabooleanfalseFill area under the line
domain[min, max] | "nice"Y-axis domain
yTicks / xTicksnumber | number[] / string[]5 / —Tick count, explicit y values, or override x labels
onClick(point, index) => voidData point click handler
xLabel / yLabelstringAxis labels
tooltipbooleantrueShow hover tooltip

PieChart props #

PropTypeDefaultDescription
dataDataPoint[]Slice data (with optional color)
sizenumber300SVG size in px (overridden when responsive)
donut / donutWidthboolean / numberfalse / 60Donut + ring thickness
showLabels / showLegendbooleanfalseShow slice labels and color legend
animatedbooleanfalseAnimate on mount
colorsstring[]Per-slice colors (overrides colorScheme)
hoverOffsetnumber0Pixels to translate slice outward on hover
selectedIndexnumber | nullControlled selected slice
onSelectedChange(index | null) => voidClick toggles selection. Provide to enable selection UI
selectedOffsetnumber8Pixels for selected slice translate
onClick(slice, index) => voidSlice click handler
tooltipbooleantrueShow hover tooltip
innerLabelReactNodeLabel 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