npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

motionchart

v0.2.0

Published

Cinematic React chart library with beautiful animations and storytelling-first charts

Readme

motionchart

Cinematic React chart library with beautiful animations and storytelling-first charts.

Built on Motion (formerly Framer Motion), motionchart renders all charts as animated SVG with zero canvas dependencies. Every chart ships with spring physics, smooth transitions, hover effects, and interactive viewport controls out of the box.

Install

npm install motionchart motion

Peer dependencies: React 18+, react-dom, motion >=11

Quick Start

import { MotionBar } from "motionchart";

const data = [
  { label: "Jan", value: 42 },
  { label: "Feb", value: 68 },
  { label: "Mar", value: 55 },
  { label: "Apr", value: 91 },
  { label: "May", value: 73 },
];

export default function SalesChart() {
  return (
    <MotionBar
      data={data}
      theme="ocean"
      showValues
      gradient={{ from: "#06b6d4", to: "#14b8a6" }}
      yAxisLabel="Revenue ($k)"
      toolbar
      responsive
    />
  );
}

Chart Types

| Chart | Component | Description | Key Props | |-------|-----------|-------------|-----------| | Bar | MotionBar | Vertical/horizontal bars, grouped, stacked | data, series, orientation, stacked, viewport | | Line | MotionLine | Single or multi-series line/area | data, showArea, curve, crosshair, viewport | | Pie | MotionPie | Pie and donut with center labels | data, innerRadius, centerLabel, cornerRadius | | Radar | MotionRadar | Spider/radar with polygon or circle grid | axes, series, levels, shape, fillOpacity | | Scatter | MotionScatter | Scatter plot with optional bubble sizing | data, dotRadius, minDotRadius, maxDotRadius | | Candlestick | MotionCandlestick | OHLC candles with volume overlay | data, bullishColor, bearishColor, showVolume | | Gantt | MotionGantt | Project timeline with dependency arrows | tasks, timelineLabels, showProgress, showDependencies | | Tree | MotionTree | Hierarchical node-link tree | data, direction, levelSpacing, branchWidth | | Treemap | MotionTreemap | Nested area treemap | data, tilePadding, borderRadius | | RadialTree | MotionRadialTree | Radial/circular tree layout | data, angularSpread, startAngle, levelSpacing | | Dendrogram | MotionDendrogram | Hierarchical clustering tree | data, direction, elbowConnectors, showScale | | Heatmap | MotionHeatmap | Row x column value matrix | data, colorRange, showColorScale, cellGap | | Funnel | MotionFunnel | Conversion funnel with stage percentages | data, showPercentage, gapBetweenStages | | Sunburst | MotionSunburst | Radial hierarchical partition | data, innerRadius, padAngle, showLabels |


Common Props (ChartBaseProps)

All charts extend ChartBaseProps unless noted otherwise (e.g. MotionPie has its own props interface but shares most options).

| Prop | Type | Default | Description | |------|------|---------|-------------| | width | number | 600 | Chart width in pixels | | height | number | 400 | Chart height in pixels | | responsive | boolean \| ResponsiveConfig | false | Fill parent container (see Responsive) | | theme | ThemeName \| Theme | "midnight" | Built-in theme name or custom theme object | | color | string | — | Single color applied to all elements | | colors | string[] | — | Color array cycled by data index | | gradient | GradientConfig | — | Global gradient { from, to, angle? } applied to all elements | | animation | AnimationConfig | — | Animation type, stiffness, duration, stagger | | hover | HoverConfig | — | Hover scale and brightness | | tooltip | boolean \| TooltipConfig | true | Built-in tooltip or custom render function | | toolbar | boolean \| ToolbarConfig | false | Export toolbar (CSV, SVG, PNG) | | showGrid | boolean | true | Show axis grid lines | | yAxisLabel | string | — | Label displayed along the y-axis | | xAxisLabel | string | — | Label displayed along the x-axis | | backgroundColor | string | — | Override theme background color | | textColor | string | — | Override theme text/label color | | gridColor | string | — | Override theme grid line color | | referenceLines | ReferenceLine[] | — | Horizontal/vertical annotation lines | | className | string | — | CSS class on the root element | | ariaLabel | string | — | ARIA label for accessibility |

AnimationConfig

| Prop | Type | Default | Description | |------|------|---------|-------------| | type | "spring" \| "tween" \| "none" | "spring" | Animation physics model | | stiffness | number | — | Spring stiffness | | damping | number | — | Spring damping | | duration | number | — | Tween duration in seconds | | ease | string | — | CSS easing function (tween only) | | staggerDelay | number | 0.04 | Delay between each element's entrance in seconds | | disabled | boolean | false | Disable all animations |

// Spring (default)
animation={{ type: "spring", stiffness: 120, damping: 14, staggerDelay: 0.05 }}

// Tween
animation={{ type: "tween", duration: 0.6, ease: "easeInOut", staggerDelay: 0.03 }}

// Disabled
animation={{ disabled: true }}

HoverConfig

| Prop | Type | Default | Description | |------|------|---------|-------------| | enabled | boolean | true | Enable hover effects | | scale | number | 1.05 | Scale factor on hover | | brightness | number | 1.15 | Brightness multiplier on hover |

Custom Tooltips

<MotionBar
  data={data}
  tooltip={{
    enabled: true,
    render: ({ label, value, color }) => (
      <div style={{ color: "#fff" }}>
        <strong style={{ color }}>{label}</strong>: {value}
      </div>
    ),
  }}
/>

Theme System

Eight built-in themes — all dark except arctic:

| Theme | Style | Background | |-------|-------|------------| | midnight | Dark violet/indigo | #0f0f1a | | sunset | Warm orange/pink | #1a0f0f | | ocean | Cyan/teal | #0f1a1a | | forest | Green | #0a1a0f | | neon | Vivid electric | #0a0a0a | | rose | Pink/red | #1a0f18 | | arctic | Light blue (light mode) | #f0f4f8 | | ember | Fire red/orange | #1c1210 |

// Built-in theme
<MotionBar data={data} theme="forest" />

// Custom theme object
import type { Theme } from "motionchart";

const myTheme: Theme = {
  name: "brand",
  background: "#1a1a2e",
  gridColor: "rgba(255,255,255,0.08)",
  textColor: "rgba(255,255,255,0.7)",
  colors: ["#e94560", "#0f3460", "#533483"],
  gradients: [{ from: "#e94560", to: "#533483" }],
  tooltipBackground: "rgba(26,26,46,0.95)",
  tooltipText: "#ffffff",
  tooltipBorder: "rgba(233,69,96,0.4)",
  glowColor: "rgba(233,69,96,0.4)",
};

<MotionBar data={data} theme={myTheme} />

Quick overrides without a full custom theme:

<MotionBar
  data={data}
  theme="midnight"
  backgroundColor="#000000"
  textColor="#ffffff"
  gridColor="rgba(255,255,255,0.1)"
/>

Color System

Colors are resolved in this priority order (highest wins):

  1. Per-item colordata[i].color or series[i].color
  2. color prop — single color string applied to all elements
  3. colors prop — array cycled by index
  4. gradient prop — global gradient applied to all elements
  5. Theme colors — active theme's color palette (fallback)
// Per-item colors
const data = [
  { label: "A", value: 30, color: "#ef4444" },
  { label: "B", value: 60, color: "#3b82f6" },
];

// Single color (overrides theme)
<MotionBar data={data} color="#6366f1" />

// Color array (cycles by index)
<MotionBar data={data} colors={["#f97316", "#22c55e", "#3b82f6"]} />

// Gradient (solid color props skip theme gradients)
<MotionBar data={data} gradient={{ from: "#6366f1", to: "#ec4899" }} />
<MotionBar data={data} gradient={{ from: "#06b6d4", to: "#14b8a6", angle: 135 }} />

Responsive

Charts fill their parent container when responsive is set. Uses ResizeObserver with debounce.

// Fill parent width, keep prop height
<div style={{ width: "100%" }}>
  <MotionBar data={data} responsive />
</div>

// Fill parent with fixed aspect ratio (height auto-derives from width)
<MotionBar data={data} responsive={{ enabled: true, aspectRatio: 16 / 9 }} />

// With constraints
<MotionBar data={data} responsive={{ enabled: true, minWidth: 300, minHeight: 200 }} />

ResponsiveConfig

| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | false | Enable responsive mode | | aspectRatio | number | — | Width / height ratio. Height auto-derives when set | | minWidth | number | 200 | Minimum width in px | | minHeight | number | 150 | Minimum height in px | | debounce | number | 50 | Resize event debounce in ms |


Viewport (Zoom and Pan)

Available on MotionBar, MotionLine, and MotionCandlestick. Auto-enables when the data count exceeds a threshold (default 20 items). Supports scroll-wheel zoom, click-drag pan, pinch-to-zoom on touch devices, and an optional minimap navigator.

// Auto-enable with defaults
<MotionBar data={largeData} viewport />

// Full config
<MotionBar
  data={largeData}
  viewport={{
    enabled: true,
    defaultRange: [0, 30],
    minimap: { height: 48, barOpacity: 0.4 },
    zoom: { min: 5, max: 100, step: 2 },
    pan: true,
    smartLabels: true,
    threshold: 20,
    touch: true,
    animationDuration: 0.3,
    onRangeChange: (range) => console.log(range),
  }}
/>

ViewportConfig

| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | auto | Force-enable or disable | | defaultRange | [number, number] | [0, threshold] | Initial visible range as [startIndex, endIndex] | | minimap | boolean \| MinimapConfig | false | Minimap strip below the chart | | zoom | boolean \| ZoomConfig | true | Scroll-wheel zoom | | pan | boolean | true | Click-drag pan | | smartLabels | boolean | true | Adaptive x-axis label density at different zoom levels | | threshold | number | 20 | Data count that triggers auto-enable | | touch | boolean | true | Pinch-to-zoom and swipe-to-pan | | animationDuration | number | 0.3 | Viewport transition duration in seconds | | onRangeChange | (range: [number, number]) => void | — | Callback fired on visible range change |

MinimapConfig

| Option | Type | Default | Description | |--------|------|---------|-------------| | height | number | 40 | Strip height in pixels | | backgroundColor | string | — | Minimap background color override | | handleColor | string | — | Viewport window border color | | trackColor | string | — | Non-selected region overlay color | | barOpacity | number | 0.4 | Minimap bar opacity |

ZoomConfig

| Option | Type | Default | Description | |--------|------|---------|-------------| | min | number | 5 | Minimum number of visible items | | max | number | total count | Maximum number of visible items | | step | number | 2 | Items to add/remove per scroll tick | | speed | number | 1 | Zoom speed multiplier |


Toolbar (Export)

A floating export button with a dropdown for CSV, SVG, and PNG downloads. Rendered as an HTML overlay so it never clips the chart SVG.

// Enable with defaults (top-right, all formats)
<MotionBar data={data} toolbar />

// Configured
<MotionBar
  data={data}
  toolbar={{
    enabled: true,
    position: "top-left",
    csv: true,
    svg: true,
    png: false,
    filename: "monthly-sales",
  }}
/>

ToolbarConfig

| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | false | Show the toolbar | | position | "top-left" \| "top-right" \| "bottom-left" \| "bottom-right" | "top-right" | Toolbar placement | | csv | boolean | true | Show CSV download button | | svg | boolean | true | Show SVG download button | | png | boolean | true | Show PNG download button | | filename | string | "chart-data" | Download filename without extension | | csvFormatter | (data: unknown) => string | — | Custom CSV serializer | | iconSize | number | 16 | Icon size in px | | backgroundColor | string | — | Toolbar background color override | | iconColor | string | — | Icon/text color override | | offset | number | 8 | Distance from chart edge in px |


Reference Lines

Annotate charts with horizontal (y-axis) or vertical (x-axis) lines for goals, averages, and thresholds.

<MotionBar
  data={data}
  referenceLines={[
    {
      value: 75,
      axis: "y",
      label: "Target",
      color: "#22c55e",
      strokeDasharray: "5 4",
      labelPosition: "right",
    },
    {
      value: 50,
      axis: "y",
      label: "Minimum",
      color: "#ef4444",
      strokeWidth: 2,
    },
  ]}
/>

ReferenceLine

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | number | required | Data value where the line is drawn | | axis | "x" \| "y" | "y" | Axis to draw on | | label | string | — | Text label beside the line | | color | string | theme textColor | Line color | | strokeWidth | number | 1.5 | Line width in px | | strokeDasharray | string | "5 4" | SVG dash pattern | | labelPosition | "left" \| "right" | "right" | Which side to place the label |


Chart Examples

MotionBar

Single series

import { MotionBar } from "motionchart";

const data = [
  { label: "Q1", value: 120 },
  { label: "Q2", value: 185 },
  { label: "Q3", value: 142 },
  { label: "Q4", value: 210 },
];

<MotionBar
  data={data}
  theme="midnight"
  gradient={{ from: "#6366f1", to: "#8b5cf6" }}
  showValues
  borderRadius={6}
  yAxisLabel="Revenue ($k)"
  toolbar
/>

Grouped multi-series

import { MotionBar } from "motionchart";

const series = [
  { label: "2023", data: [120, 185, 142, 210] },
  { label: "2024", data: [145, 200, 168, 250] },
];
const labels = ["Q1", "Q2", "Q3", "Q4"];

<MotionBar
  series={series}
  labels={labels}
  theme="ocean"
  legend
  showValues
  yAxisLabel="Revenue ($k)"
/>

Stacked with large dataset and viewport

<MotionBar
  series={series}
  labels={labels}
  theme="sunset"
  stacked
  legend
  showValues
  borderRadius={4}
  viewport={{ minimap: true, zoom: true }}
/>

MotionBar-specific props

| Prop | Type | Default | Description | |------|------|---------|-------------| | data | BarDataPoint[] | — | Single series data | | series | BarSeries[] | — | Multi-series grouped/stacked data | | labels | string[] | — | X-axis labels for multi-series mode | | orientation | "vertical" \| "horizontal" | "vertical" | Bar orientation | | stacked | boolean | false | Stack bars instead of grouping | | sort | "asc" \| "desc" \| "none" | "none" | Sort bars by value | | borderRadius | number | 4 | Bar corner radius in px | | barPadding | number | 0.3 | Space between bars (0-1) | | groupPadding | number | 0.1 | Space within groups (0-1) | | barWidth | number | — | Fixed bar width in px (overrides barPadding) | | showValues | boolean | false | Show value labels above bars | | showLabels | boolean | true | Show x-axis labels | | yTickCount | number | 5 | Number of y-axis tick marks | | legend | boolean \| LegendConfig | — | Show legend | | viewport | ViewportConfig | — | Zoom/pan viewport for large datasets | | onBarClick | (params: BarClickParams) => void | — | Click callback |


MotionLine

Multi-series with area fill and viewport

import { MotionLine } from "motionchart";

const data = [
  {
    label: "Revenue",
    data: [
      { x: "Jan", y: 42 }, { x: "Feb", y: 68 },
      { x: "Mar", y: 55 }, { x: "Apr", y: 91 },
    ],
    color: "#6366f1",
  },
  {
    label: "Expenses",
    data: [
      { x: "Jan", y: 30 }, { x: "Feb", y: 45 },
      { x: "Mar", y: 40 }, { x: "Apr", y: 60 },
    ],
    color: "#f97316",
  },
];

<MotionLine
  data={data}
  theme="midnight"
  showArea
  curve="smooth"
  legend
  crosshair
  yAxisLabel="Amount ($k)"
  viewport={{ minimap: true }}
  responsive
/>

MotionLine-specific props

| Prop | Type | Default | Description | |------|------|---------|-------------| | data | LineSeries \| LineSeries[] | required | Line series data | | curve | "smooth" \| "linear" \| "step" | "smooth" | Curve interpolation | | drawSpeed | number | 1.8 | Line draw animation duration in seconds | | showDots | boolean | true | Show data point dots | | dotRadius | number | 5 | Dot size in px | | dotScale | number | — | Dot scale on hover | | showArea | boolean | false | Fill area under the line | | showXLabels | boolean | true | Show x-axis labels | | showYLabels | boolean | true | Show y-axis labels | | yMin | number \| "auto" | 0 | Minimum y-axis value | | crosshair | boolean | true | Vertical crosshair with multi-series tooltip | | legend | boolean \| LegendConfig | auto | Auto-enabled for multi-series | | viewport | ViewportConfig | — | Zoom/pan viewport for large datasets | | onPointClick | (params: LineClickParams) => void | — | Click callback |


MotionPie

Donut with center label

import { MotionPie } from "motionchart";

const data = [
  { label: "Direct", value: 42 },
  { label: "Organic", value: 28 },
  { label: "Referral", value: 18 },
  { label: "Social", value: 12 },
];

<MotionPie
  data={data}
  theme="ocean"
  innerRadius={0.6}
  centerLabel="Total"
  centerSubLabel="100 sessions"
  cornerRadius={4}
  padAngle={2}
  showLabels
  legend
/>

MotionPie-specific props

| Prop | Type | Default | Description | |------|------|---------|-------------| | data | PieDataPoint[] | required | Slice data | | innerRadius | number | 0 | Donut hole ratio (0 = full pie, 0-1 = fraction of outer radius) | | cornerRadius | number | 0 | Rounded slice edges in px | | padAngle | number | 0 | Gap between slices in degrees | | startAngle | number | -90 | Start angle in degrees (default: 12 o'clock) | | showLabels | boolean | false | Show percentage labels on slices | | showValues | boolean | false | Show raw value labels on slices | | centerLabel | string | — | Center text (donut charts only) | | centerSubLabel | string | — | Secondary text below centerLabel | | strokeColor | string | theme background | Stroke color between slices | | strokeWidth | number | — | Stroke width between slices | | legend | boolean \| LegendConfig | — | Show legend | | onSliceClick | (params: PieClickParams) => void | — | Click callback |


MotionScatter

Multi-series bubble chart

import { MotionScatter } from "motionchart";

const data = [
  {
    label: "Product A",
    data: [
      { x: 20, y: 85, size: 12, label: "Launch" },
      { x: 45, y: 72, size: 8 },
      { x: 70, y: 91, size: 15, label: "Peak" },
    ],
  },
  {
    label: "Product B",
    data: [
      { x: 15, y: 60, size: 6 },
      { x: 55, y: 48, size: 10 },
      { x: 80, y: 75, size: 9 },
    ],
  },
];

<MotionScatter
  data={data}
  theme="neon"
  legend
  minDotRadius={4}
  maxDotRadius={20}
  dotOpacity={0.75}
  xAxisLabel="Cost ($)"
  yAxisLabel="Performance"
  yMin="auto"
/>

MotionScatter-specific props

| Prop | Type | Default | Description | |------|------|---------|-------------| | data | ScatterSeries \| ScatterSeries[] | required | Scatter data | | dotRadius | number | 5 | Default dot radius in px | | minDotRadius | number | 3 | Min radius for bubble mode (size dimension) | | maxDotRadius | number | 20 | Max radius for bubble mode | | dotOpacity | number | 0.7 | Dot fill opacity | | showXLabels | boolean | true | Show x-axis labels | | showYLabels | boolean | true | Show y-axis labels | | yMin | number \| "auto" | 0 | Minimum y-axis value | | legend | boolean \| LegendConfig | — | Show legend | | onPointClick | (params: ScatterClickParams) => void | — | Click callback |


MotionHeatmap

Correlation matrix with color scale

import { MotionHeatmap } from "motionchart";

const data = [
  { row: "Mon", col: "9am",  value: 12 },
  { row: "Mon", col: "10am", value: 45 },
  { row: "Mon", col: "11am", value: 68 },
  { row: "Tue", col: "9am",  value: 34 },
  { row: "Tue", col: "10am", value: 72 },
  { row: "Tue", col: "11am", value: 55 },
];

<MotionHeatmap
  data={data}
  theme="midnight"
  colorRange={["#1e1b4b", "#6366f1"]}
  showValues
  showColorScale
  cellRadius={4}
  cellGap={3}
/>

MotionHeatmap-specific props

| Prop | Type | Default | Description | |------|------|---------|-------------| | data | HeatmapDataPoint[] | required | Grid data (row, col, value) | | cellRadius | number | 2 | Cell corner radius in px | | cellGap | number | 2 | Gap between cells in px | | showValues | boolean | false | Show numeric values inside cells | | colorRange | [string, string] | theme colors | Low-to-high color interpolation | | showXLabels | boolean | true | Show column labels | | showYLabels | boolean | true | Show row labels | | showColorScale | boolean | true | Show color scale legend bar | | colorScaleWidth | number | 16 | Color scale bar width in px | | onCellClick | (params: HeatmapClickParams) => void | — | Click callback |


MotionCandlestick

OHLC chart with volume and reference lines

import { MotionCandlestick } from "motionchart";

const data = [
  { label: "Jan 2", open: 148, high: 153, low: 145, close: 151, volume: 8200000 },
  { label: "Jan 3", open: 151, high: 156, low: 149, close: 154, volume: 9100000 },
  { label: "Jan 4", open: 154, high: 155, low: 146, close: 148, volume: 11500000 },
  { label: "Jan 5", open: 148, high: 152, low: 143, close: 150, volume: 7800000 },
  { label: "Jan 8", open: 150, high: 158, low: 149, close: 157, volume: 9600000 },
];

<MotionCandlestick
  data={data}
  theme="midnight"
  bullishColor="#22c55e"
  bearishColor="#ef4444"
  showVolume
  volumeHeight={0.2}
  viewport={{ minimap: true, zoom: true }}
  referenceLines={[
    { value: 155, axis: "y", label: "Resistance", color: "#f97316", strokeDasharray: "6 3" },
    { value: 147, axis: "y", label: "Support",    color: "#22c55e", strokeDasharray: "6 3" },
  ]}
  toolbar
/>

MotionCandlestick-specific props

| Prop | Type | Default | Description | |------|------|---------|-------------| | data | CandlestickDataPoint[] | required | OHLC data | | bullishColor | string | theme color | Color for candles where close > open | | bearishColor | string | theme color | Color for candles where close < open | | candleWidth | number | 0.6 | Candle body width as fraction of slot (0-1) | | wickWidth | number | 1.5 | Wick line width in px | | showXLabels | boolean | true | Show x-axis labels | | showYLabels | boolean | true | Show y-axis price labels | | showVolume | boolean | false | Show volume bars below the chart | | volumeHeight | number | 0.2 | Volume sub-chart height fraction (0-1) | | volumeOpacity | number | 0.3 | Volume bar opacity | | viewport | ViewportConfig | — | Zoom/pan viewport for large datasets | | onCandleClick | (params: CandleClickParams) => void | — | Click callback |


Other Charts

MotionRadar

import { MotionRadar } from "motionchart";

<MotionRadar
  axes={[
    { label: "Speed" },
    { label: "Shooting" },
    { label: "Passing" },
    { label: "Defense" },
    { label: "Stamina" },
  ]}
  series={[
    { label: "Player A", data: [88, 92, 78, 60, 85] },
    { label: "Player B", data: [70, 65, 90, 88, 72] },
  ]}
  theme="midnight"
  shape="polygon"
  levels={5}
  fillOpacity={0.3}
  legend={{ position: "bottom" }}
/>

MotionGantt

import { MotionGantt } from "motionchart";

<MotionGantt
  tasks={[
    { id: "design", label: "UI Design", start: 0, end: 4, progress: 1, group: "Design" },
    { id: "dev", label: "Development", start: 4, end: 10, progress: 0.4, group: "Engineering", dependencies: ["design"] },
    { id: "qa", label: "QA", start: 9, end: 12, progress: 0, group: "Engineering", dependencies: ["dev"] },
  ]}
  theme="midnight"
  showProgress
  showDependencies
  showPercentage
/>

MotionTreemap

import { MotionTreemap } from "motionchart";

<MotionTreemap
  data={{
    label: "Portfolio",
    children: [
      {
        label: "Tech",
        children: [
          { label: "Apple", value: 180 },
          { label: "Google", value: 140 },
        ],
      },
      {
        label: "Finance",
        children: [
          { label: "JPMorgan", value: 120 },
          { label: "Visa", value: 70 },
        ],
      },
    ],
  }}
  theme="sunset"
  showLabels
  showValues
  legend
/>

MotionFunnel

import { MotionFunnel } from "motionchart";

<MotionFunnel
  data={[
    { label: "Visitors",  value: 12000 },
    { label: "Sign Ups",  value: 7500 },
    { label: "Trial",     value: 4200 },
    { label: "Paid",      value: 1800 },
  ]}
  theme="sunset"
  showPercentage
  showValues
/>

MotionSunburst

import { MotionSunburst } from "motionchart";

<MotionSunburst
  data={{
    label: "World",
    children: [
      {
        label: "Americas",
        children: [
          { label: "USA", value: 330 },
          { label: "Brazil", value: 210 },
        ],
      },
      {
        label: "Europe",
        children: [
          { label: "Germany", value: 83 },
          { label: "France", value: 67 },
        ],
      },
    ],
  }}
  theme="ocean"
  showLabels
  legend={{ position: "bottom" }}
/>

Legend

Charts with multiple series support a configurable legend.

// Enable with defaults
<MotionBar series={series} labels={labels} legend />

// Positioned legend
<MotionBar series={series} labels={labels} legend={{ position: "bottom", layout: "horizontal" }} />

LegendConfig

| Option | Type | Default | Description | |--------|------|---------|-------------| | position | "top" \| "bottom" \| "left" \| "right" | "bottom" | Legend placement | | layout | "horizontal" \| "vertical" \| "auto" | "auto" | Item layout direction |


TypeScript

All types are exported from the package root:

import type {
  BarDataPoint,
  BarSeries,
  LineSeries,
  LineDataPoint,
  PieDataPoint,
  ScatterSeries,
  ScatterDataPoint,
  CandlestickDataPoint,
  GanttTask,
  TreeNode,
  HeatmapDataPoint,
  FunnelStage,
  RadarDataPoint,
  RadarSeries,
  GradientConfig,
  AnimationConfig,
  HoverConfig,
  TooltipConfig,
  ViewportConfig,
  ResponsiveConfig,
  ToolbarConfig,
  ReferenceLine,
  LegendConfig,
  ThemeName,
  Theme,
  ChartBaseProps,
} from "motionchart";

Shared Data Types

// Used by Tree, Treemap, RadialTree, Dendrogram, Sunburst
interface TreeNode {
  label: string;
  value?: number;          // area in treemap, branch distance in dendrogram
  color?: string;
  gradient?: GradientConfig;
  children?: TreeNode[];
}

interface GradientConfig {
  from: string;
  to: string;
  angle?: number;          // rotation in degrees
}

Built-in Formatters

motionchart ships with locale-aware formatter factories so you don't have to write (v) => $${v.toLocaleString()} boilerplate. Each returns a function ready to drop into yTickFormat or xTickFormat. All built on native Intl.NumberFormat / Intl.DateTimeFormat — zero dependencies, full locale support.

import {
  MotionBar,
  formatCurrency,
  formatCompact,
  formatPercent,
  formatDate,
} from "motionchart";

<MotionBar yTickFormat={formatCurrency("USD")} />
<MotionBar yTickFormat={formatCompact()} />
<MotionLine yTickFormat={formatPercent({ decimals: 1 })} />
<MotionCandlestick xTickFormat={formatDate("monthYear")} />

Number formatters

| Formatter | Output (en-US) | Options | |-----------|----------------|---------| | formatNumber() | 1,234,567 | { decimals, locale } | | formatCompact() | 1.2M, 2.5B, 42 | { decimals, locale } (default decimals: 1) | | formatCurrency("USD") | $1,234.56 | { decimals, locale, display, compact } | | formatPercent() | 42% | { decimals, locale, asRatio } | | formatScientific() | 1.23E6 | { decimals, locale } (default decimals: 2) |

formatPercent modes:

  • Default (chart-style): input is already a percentage → formatPercent()(42)"42%"
  • asRatio: true (Intl convention): input is a 0–1 ratio → formatPercent({ asRatio: true })(0.42)"42%"

formatCurrency examples:

formatCurrency("USD")(1234.56)                    // "$1,234.56"
formatCurrency("EUR", { locale: "de-DE" })(1234)  // "1.234,00 €"
formatCurrency("USD", { compact: true })(1.2e6)   // "$1.2M"
formatCurrency("JPY")(1234)                       // "¥1,234"

Date formatters

formatDate accepts a preset name or raw Intl.DateTimeFormatOptions. Input can be a Date, Unix ms number, or ISO string.

| Preset | Output (en-US) | |--------|----------------| | "short" | 1/15/24 | | "medium" | Jan 15, 2024 | | "long" | January 15, 2024 | | "monthYear" | Jan 2024 | | "monthYearLong" | January 2024 | | "monthOnly" | Jan | | "yearOnly" | 2024 | | "weekday" | Mon | | "weekdayLong" | Monday | | "time" | 10:30 AM | | "timeWithSeconds" | 10:30:45 AM |

formatDate("monthYear")(new Date(2024, 0, 15))         // "Jan 2024"
formatDate("monthYear", { locale: "fr-FR" })(...)      // "janv. 2024"
formatDate({ year: "numeric", month: "narrow" })(...)  // "J 2024"

SSR (Server-Side Rendering)

motionchart works with Next.js, Remix, Astro, Gatsby, and any React framework that pre-renders on the server. Every chart component serializes to valid HTML/SVG via renderToString — verified by 17 SSR tests in the test suite.

Next.js App Router (recommended):

// app/dashboard/page.tsx
"use client";

import { MotionBar } from "motionchart";

export default function Dashboard() {
  return <MotionBar data={data} />;
}

motionchart components use React hooks internally, so they require the "use client" directive — same as Recharts, Nivo, and any interactive chart library.

Next.js Pages Router / Remix:

import dynamic from "next/dynamic";

const MotionBar = dynamic(
  () => import("motionchart").then((m) => m.MotionBar),
  { ssr: false },
);

Astro:

---
import { MotionBar } from "motionchart";
---
<MotionBar data={data} client:only="react" />

Behavior notes:

  • ARIA labels are serialized into server HTML (good for SEO and screen readers)
  • When responsive is enabled, charts render at the prop dimensions on the server and resize to fit the container after hydration — the same brief layout reflow as every other chart library
  • Animations only start after the client takes over (expected — Node has no animation frame)

Build Output

| Format | File | |--------|------| | ESM | dist/index.mjs | | CJS | dist/index.cjs | | TypeScript declarations | dist/index.d.ts |

Built with tsup.


License

MIT