Skip to content
microcharts
ReferenceDecisionRateVolume

RateVolume

React rate-volume chart — rate and volume together without dual-axis lies. Tiny accessible SVG microchart; RSC-safe static or /interactive.

RateVolume answers "the rate moved — on what volume?". A precise rate line rides over deliberately low-precision ghost volume bars — the denominator. There is no prop to remove the bars: a rate without its denominator is the lie this type exists to prevent. A 4.1% conversion rate reads very differently on 38 events than on 3,800.

RateVolume
interactive · 5.37 kB · static · 2.85 kB

Install

Import & usereact/rate-volume
import { RateVolume } from "@microcharts/react/rate-volume";// periods — real values under “Sample data” below<RateVolume data={periods} minVolume={50} title="Conversion rate" />
Sample data
const periods = [  { rate: 0.023, volume: 220 }, { rate: 0.025, volume: 190 }, { rate: 0.028, volume: 160 },  { rate: 0.029, volume: 130 }, { rate: 0.031, volume: 110 }, { rate: 0.034, volume: 90 },  { rate: 0.036, volume: 66 }, { rate: 0.041, volume: 38 },];
Needs package + stylesheetSet up with AI

Try it

When to use it

  • Good for — a conversion / error rate with its denominator, a KPI card where the rate is the headline, spotting a big rate move on thin volume.
  • Avoid for — volume itself needing a precise read (pair a SparkBar) or a plain series (Sparkline).

Sizing

Variants

low-volume flag + step
locale

format and volumeFormat both take Intl.NumberFormatOptions and share the same locale — the rate label reads "4,1 %" and volumes group with a period ("3.800") under de-DE, matching that locale's own conventions rather than a hardcoded English format.

Edge cases

a zero-volume period breaks the line
empty data

A period with zero (or non-finite) volume renders no bar and no rate mark, and the line gaps across it instead of interpolating through a period nothing was measured on — the interactive entry announces that period as "no events" rather than a rate. With no data at all, the chart renders an empty root and the accessible name says "No data."

Four homes

Why this default

The volume bars are muted and unlabeled because they answer "enough?", not "how many?". A rate on zero volume is undefined regardless of the input — the line breaks and no mark is drawn, because a rate nobody generated should never be plotted. minVolume makes a thin denominator visible right at the mark: below it the rate point renders hollow, a shape cue that survives forced-colors and print.

Accessibility

The accessible name always pairs the rate with its volume — "4.1% on 38 events (low volume); up from 2.3% across 5 periods.". The interactive entry steps the periods and its live region never states a rate without the volume it stands on, announcing "no events" for a zero-volume period.

The interactive entry follows the shared interaction contract: arrow keys rove between units on both axes, Home and End jump to the ends, and a click, tap, Enter or Space selects a unit — pinning its readout so it survives blur, until you select it again or press Escape. On touch, a tap pins and a drag scrubs.

Props

PropTypeNotes
data*{ rate; volume }[]Periods, oldest first — each a rate and the volume it was measured on.
minVolumenumberBelow it, the rate mark renders hollow — 'insufficient denominator'.
curve"linear" | "step"Step suits per-period aggregate rates.
volumeFormatIntl.NumberFormatOptions | (n) => stringVolume has different units than rate; formatted separately.
unitstringNoun for the volume unit in the summary (default 'events').
volumeDomain[number, number]Volume (bar) domain — defaults to [0, max], zero-anchored.
label"last" | "none"Endpoint rate in a right gutter.
animatebooleaninteractiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion.

Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.

Related charts