Area chart React component for Tailwind CSS v4
v1.0.0Added in 1.0.0
Volumes over time as filled areas: overlapping gradients, stacked bands for parts of a total, or 100% stacked shares.
Move the mouse over the plot for a crosshair, a dot on every band and a readout of every visible series (stacked charts list the top band first and add a Total row; percent charts show the value and its share, "$48,920 · 38.1%"). On a phone, tap a month to pin the readout above your finger, drag sideways to scrub (vertical drags still scroll the page), tap the same month again or anywhere outside to clear it. With the keyboard, Tab to the plot, then Left / Right walk the points, Home / End jump to the ends, PageUp / PageDown move by ten, Enter fires onDatumClick, Escape clears; a live region reads each point. Legend entries are buttons: click or tap one to hide or show its series (the stack re-flows with a glide), hover or focus one to highlight it. Switching type between overlap, stacked and percent glides the areas to their new shape and cross-fades the y ticks. Areas rise from the baseline and the lines draw in the first time the chart scrolls into view; reduced motion shows the final chart at once.
Area chart is a Pro React component in the React chart and stat components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add area-chart, together with Chart kit, Motion presets, UIProvider and useLocale and the npm packages motion, d3-shape and d3-scale.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add area-chartArea chart is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import AreaChart from @/components/ui/area-chart.
import { AreaChart } from "@/components/ui/area-chart"
const data = [
{ month: "2026-07", direct: 41460, online: 28700, partners: 18070 },
{ month: "2026-08", direct: 38030, online: 27300, partners: 15600 },
{ month: "2026-09", direct: 48920, online: 36400, partners: 22130 },
]
export function RevenueByChannel() {
return (
<AreaChart
data={data}
xKey="month"
series={[
{ key: "direct", label: "Direct sales" },
{ key: "online", label: "Online store" },
{ key: "partners", label: "Partners" },
]}
type="stacked"
valueFormat="currency"
currency="USD"
label="Revenue by channel, 2026"
/>
)
}Props
data*ChartDatum[]One row per x position: { month: "2026-09", direct: 48920, online: 36400 }. Data order is x order (the chart does not sort).
xKeydefault"date"stringThe row key for the x axis. ISO days ("2026-09-24") and months ("2026-09") print as local dates.
seriesAreaChartSeries[]Which keys to draw ({ key, label?, color?, name? }), in color and stack order: series 1 is the bottom band. Inferred from the numeric keys of the first rows when omitted.
colorsstring[]Per-position palette overrides: slots ("series-3"), tones ("neutral", "brand", "success") or any css color.
typedefault"overlap""overlap" | "stacked" | "percent"overlap: every area from zero, the first series on top. stacked: bands add up (negative values stack below zero, diverging). percent: 100% stacked shares, y axis 0 to 100 %, the readout and table add each share with one decimal ("38,1 %"), negatives drawn as 0.
filldefault"gradient""gradient" | "solid" | "none"gradient fades the series color from fillOpacity to transparent; solid is a flat fill at fillOpacity; none draws lines only (always on, whatever showLine says).
fillOpacitydefault0.22numberThe top of the gradient or the solid fill. Stacked bands add 0.1 so they read as bands; overlapping fills of two or more series never exceed 0.22.
showLinedefaulttruebooleanThe line on each area's top edge. Stacked bands keep a thin surface gap between them without it.
curvedefault"monotone""monotone" | "linear" | "natural" | "step"How points are joined. monotone never overshoots the data.
strokeWidthdefault2numberWidth of the top lines in px.
connectNullsdefaultfalsebooleanBridge null gaps instead of breaking the area (overlap only; stacked types treat nulls as 0).
heightdefault240numberPlot height in px, reserved before measuring so nothing jumps. The width always fills the parent.
valueFormatdefault"number"ChartValueFormat = "number" | "compact" | "currency" | "percent"How values print in the readout, the ticks and the table: "128,450", "128K", "$128,450", "12.4%" (percent takes whole numbers). Ticks go compact on their own from 10,000.
currencydefault"USD"stringISO 4217 code for valueFormat="currency", printed with its narrow symbol ("$", "€"). Anything that is not a three-letter code prints as plain numbers with the text as a suffix.
decimalsnumberFraction digits in the readout and the table (ticks stay short). Auto by magnitude.
formatValue(value: number, context: { series: string; kind: "tooltip" | "tick" | "label" | "table" }) => stringFull control over value text, e.g. v => `${v} units`. Percent ticks and shares keep the kit format.
formatX(value: unknown, context: "axis" | "tooltip" | "table") => stringx labels; default formatChartLabel ("Sep" on the axis, "September 2026" in the readout).
localedefault""stringIntl locale for numbers and dates; empty uses UIProvider's locale, then the page's.
yDomaindefault"["auto", "auto"]"AreaChartDomain = [number | "auto", number | "auto"]Fixed value bounds. Areas always include zero; percent charts are always 0 to 100.
showGriddefaulttruebooleanHairlines at the value ticks.
showXAxisdefaulttruebooleanx labels under the plot, thinned from the newest backwards so they never collide.
showYAxisdefaulttruebooleanValue tick labels.
yAxisSidedefault"start""start" | "end"end puts the value ticks on the right (finance style).
showLegenddefaulttruebooleanThe legend; only rendered with two or more series (one series is named by the card title).
legendPositiondefault"top""top" | "bottom"Legend above or below the plot. It wraps on narrow widths.
legendToggledefaulttruebooleanLegend entries are buttons that hide and show their series (the last visible series cannot be hidden).
showTooltipdefaulttruebooleanThe floating readout. When off, the crosshair, keyboard and live region still work.
tooltipModedefault"auto""auto" | "float" | "fixed"float rides beside the crosshair, fixed pins above the plot; auto is fixed on touch screens.
referenceLines{ value: number; label?: string; color?: string }[]Dashed targets or averages ("Media 261"). In percent charts the value is a share (0 to 100).
animateddefaulttruebooleanAreas rise and lines draw the first time the chart scrolls into view; data, type and legend changes glide.
loadingdefaultfalsebooleanA skeleton in the plot's shape, same height.
emptyStateReactNodeShown when data is empty or every value is null. Default: an icon tile and labels.empty.
labeldefault""stringAccessible name of the figure and caption of its data table. Empty joins the series names.
descriptiondefault""stringA one-sentence takeaway for screen readers (aria-describedby).
surfacedefault""stringThe color behind the chart, used for dot rings and the gaps between stacked bands. Empty = var(--surface).
hiddenSeriesstring[]Hidden series keys (controlled). Pair with onHiddenSeriesChange.
defaultHiddenSeriesdefault"[]"string[]Series hidden at first (uncontrolled).
onHiddenSeriesChange(keys: string[]) => voidCalled when a legend toggle hides or shows a series.
activeIndexnumber | nullThe inspected x index (controlled), so two charts can share one crosshair.
defaultActiveIndexdefault"null"number | nullThe inspected index at first (uncontrolled).
onActiveIndexChange(index: number | null) => voidCalled while hovering, scrubbing or walking with the keyboard.
onDatumClick(event: { index: number; datum: ChartDatum; series?: string }) => voidA click on the plot (series = the band under the pointer) or Enter on the inspected point (series = the first visible one).
labelsPartial<AreaChartLabels>English defaults: empty, loading, table, total, other, inspectHint, showSeries, hideSeries and shareColumn (the percent table's share column).
classNamesPartial<Record<"legend" | "plot" | "tooltip" | "table" | "area" | "line" | "dot" | "grid" | "xAxis" | "yAxis", string>>Extra classes for the inner parts.
classNamestringExtra classes for the figure root, merged last.
Parts
DEFAULT_AREA_CHART_LABELS
constantThe English strings the chart uses (empty and loading text, the data table, the keyboard hint, show and hide series, the share column of a percent chart). Spread it and override what you translate.
AreaChartLabels
typeThe shape of labels: the chart kit's shared strings plus shareColumn(series) for the percent table.
AreaChartSeries
typeOne drawn key: { key, label?, color?, name? }, in color and stack order.
Examples
Overlap of two
Collected vs. expenses as overlapping gradients.
100% stacked
Channel shares per month, y axis 0 to 100%.
Solid bands, no lines
Stock per warehouse, weekly, with a custom value format.
Neutral with an average
A single monochrome area and a dashed reference line.
Loading and empty
The skeleton and the empty state keep the plot's height.
Phone
Five stacked series at 360px with the pinned readout.
Files
- components/ui/area-chart.tsx
FAQ
Is the React Area chart component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Area chart?
Run npx wingo-ui@latest add area-chart. It also adds the Wingo UI items it builds on (Chart kit, Motion presets, UIProvider and useLocale) and the npm packages motion, d3-shape and d3-scale. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/area-chart.
How does Area chart behave on phones?
On a phone, tap a month to pin the readout above your finger, drag sideways to scrub (vertical drags still scroll the page), tap the same month again or anywhere outside to clear it.
See also
Used in
More React chart and stat components
Articles using this component
- React Charts Tailwind: 4 Charts and a Kit for Any Theme
Component guides10 min read
- Best React Component Library for Dashboard Apps in 2026
Comparisons and alternatives13 min read