Skip to content
microcharts
ReferenceDecisionForecastCone

ForecastCone

React forecast cone chart — history plus a widening prediction cone for landing risk. Tiny accessible SVG microchart; RSC-safe static or /interactive.

ForecastCone answers "will we land where we need to?". History is a solid line; the forecast is a fan of prediction bands that widen over the horizon, with a dashed median — because an estimate should never render as fact. The whole point of a fan chart is visible confidence decay, so three rules are not style options: at most two bands (50/80 — a 95% band reads as false tail confidence at micro scale), the median is always dashed, and a cone that fails to widen is flagged, never quietly inflated.

ForecastCone
interactive · 5.97 kB · static · 3.58 kB

Install

Import & usereact/forecast-cone
import { ForecastCone } from "@microcharts/react/forecast-cone";// history, forecast — real values under “Sample data” below<ForecastCone data={history} forecast={forecast} target={45} title="Q4 revenue" />
Sample data
// weekly revenue ($M): 7 weeks of history, a 4-week widening forecastconst history = [30, 32, 31, 34, 36, 35, 38];const forecast = {  mid: [39, 40, 41, 42],  p80: [[36, 42], [35, 45], [34, 50], [33, 55]],  p50: [[37, 41], [37, 43], [36, 46], [35, 49]],};
Needs package + stylesheetSet up with AI

Try it

When to use it

  • Good for — a "will we hit Q4?" forecast in a KPI card, a projection with honest uncertainty in a sentence, band-vs-target landing reads.
  • Avoid for — a forecast with no uncertainty (Sparkline) or one estimate's spread (GradedBand).

Sizing

Variants

single band + no target
locale

format also takes Intl.NumberFormatOptions — with a locale, the in-chart landing label and the accessible summary's median, interval, and target numbers all follow that locale's own grouping and currency placement (the label above reads "4.200 €", not "€4,200").

Edge cases

no history yet
a cone that fails to widen

With no history the cone still draws from the first forecast point, and the accessible summary quietly drops its "from N today" clause instead of naming a value that doesn't exist. A cone whose bands narrow instead of widen is an input-honesty failure — it renders exactly as supplied (never auto-inflated to look right) and logs a one-time dev warning so the mistake surfaces in development rather than silently misleading a reader.

Four homes

Why this default

50/80 bands, not 95, because micro-scale tails invite overreading — a 95% band paints false confidence about the extremes. The median is dashed so it never reads as a committed number, and the cone must visibly widen: if the supplied bands don't (they narrow, or stay flat), that is an input-honesty failure the component surfaces rather than papering over — a forecast whose uncertainty doesn't grow with the horizon is misrepresenting confidence decay.

Accessibility

The accessible name states the median, the horizon interval, today's actual, and — with a target — whether the band clears it: "Median forecast 42 by week 11 (80% between 33 and 55), from 38 today. The 80% band straddles the 50 target.". The interactive entry is region-aware: history points announce a value, forecast points announce the median and 80% interval.

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*number[]Historical actuals.
forecast*{ mid: number[]; p80: [lo,hi][]; p50?: [lo,hi][] }Median + prediction bands (at most 2: 50/80).
targetnumberThe landing reference the cone must clear (adds a clearance clause).
unitstringPeriod noun for the summary (default "week").
label"landing" | "none"Median endpoint value 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