ComponentsArea Chart
Area Chart
Volume over time on a single y-axis. Stack series to show how parts build a total, or overlap them to compare shapes. The crosshair, keyboard readout and table view work exactly as in Line Chart.
On this page
Install component source from the public registry, or use the local workspace package. Follow the setup guide first. The npm package is not published yet.
View registry filesImplemented with React and browser APIs, without an additional behavior or rendering library.
Shared foundation: React, clsx for conditional classes, tailwind-merge for resolving utility-class conflicts. Styles use Tailwind CSS and Dethink CSS variables.
Based on this component’s source and registry composition. Libraries used only in showcase examples are excluded.
Import the component into your page or component file.
import { AreaChart } from "@dethink/components";A stacked composition, two overlapping series, and a single series with gaps.
Stacked MRR by plan
The top edge reads as total MRR. The tooltip, readout and table list each plan's own value, not the cumulative one. Hiding a plan restacks the rest without repainting them.
MRR by plan, 2026
| Month | Starter | Growth | Enterprise |
|---|---|---|---|
| Jan | $8,200 | $21.4K | $18K |
| Feb | $8,600 | $22.1K | $18K |
| Mar | $9,100 | $23.8K | $22.5K |
| Apr | $9,300 | $24.6K | $22.5K |
| May | $9,900 | $25.2K | $22.5K |
| Jun | $10.4K | $27.1K | $26K |
| Jul | $10.2K | $28.4K | $26K |
| Aug | $10.9K | $29K | $30.5K |
| Sep | $11.6K | $31.2K | $30.5K |
| Oct | $11.8K | $32.6K | $30.5K |
| Nov | $12.3K | $33.1K | $36K |
| Dec | $12.9K | $35.4K | $36K |
Series: Starter, Growth, Enterprise. 12 points from Jan to Dec. Use the arrow keys, Home and End to read values.
Show sourceexamples/area-chart/mrr.tsx
"use client";
import { AreaChart } from "@dethink/components";
const months = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
const starter = [
8_200, 8_600, 9_100, 9_300, 9_900, 10_400, 10_200, 10_900, 11_600, 11_800,
12_300, 12_900,
];
const growth = [
21_400, 22_100, 23_800, 24_600, 25_200, 27_100, 28_400, 29_000, 31_200,
32_600, 33_100, 35_400,
];
const enterprise = [
18_000, 18_000, 22_500, 22_500, 22_500, 26_000, 26_000, 30_500, 30_500,
30_500, 36_000, 36_000,
];
const data = months.map((month, i) => ({
month,
starter: starter[i],
growth: growth[i],
enterprise: enterprise[i],
}));
export function AreaChartMrr() {
return (
<div className="w-full">
<h3 id="mrr-heading" className="mb-3 text-sm font-medium">
MRR by plan, 2026
</h3>
<AreaChart
aria-labelledby="mrr-heading"
stacked
data={data}
index="month"
indexLabel="Month"
series={[
{ key: "starter", label: "Starter" },
{ key: "growth", label: "Growth" },
{ key: "enterprise", label: "Enterprise" },
]}
formatOptions={{ style: "currency", currency: "USD" }}
/>
</div>
);
}Overlapping series
Washes are drawn first and every line on top, so no fill hides another series' edge. Use two or three series at most when overlapping.
Sessions by device, this week
| Day | Desktop | Mobile |
|---|---|---|
| Mon | 4,820 | 2,310 |
| Tue | 5,140 | 2,450 |
| Wed | 5,390 | 2,620 |
| Thu | 5,210 | 2,580 |
| Fri | 4,760 | 2,940 |
| Sat | 2,130 | 3,710 |
| Sun | 1,980 | 3,880 |
Series: Desktop, Mobile. 7 points from Mon to Sun. Use the arrow keys, Home and End to read values.
Show sourceexamples/area-chart/sessions.tsx
"use client";
import { AreaChart } from "@dethink/components";
const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const desktop = [4_820, 5_140, 5_390, 5_210, 4_760, 2_130, 1_980];
const mobile = [2_310, 2_450, 2_620, 2_580, 2_940, 3_710, 3_880];
const data = days.map((day, i) => ({
day,
desktop: desktop[i],
mobile: mobile[i],
}));
export function AreaChartSessions() {
return (
<div className="w-full">
<h3 id="sessions-heading" className="mb-3 text-sm font-medium">
Sessions by device, this week
</h3>
<AreaChart
aria-labelledby="sessions-heading"
data={data}
index="day"
indexLabel="Day"
height={240}
series={[
{ key: "desktop", label: "Desktop" },
{ key: "mobile", label: "Mobile" },
]}
/>
</div>
);
}Single series with gaps
One series needs no legend. Missing readings leave a gap in both the wash and the line.
Daily active users
| Day | Active users |
|---|---|
| Sep 1 | 1,210 |
| Sep 2 | 1,260 |
| Sep 3 | 1,190 |
| Sep 4 | 1,320 |
| Sep 5 | 1,410 |
| Sep 6 | 1,380 |
| Sep 7 | — |
| Sep 8 | — |
| Sep 9 | 1,520 |
| Sep 10 | 1,610 |
| Sep 11 | 1,580 |
| Sep 12 | 1,690 |
| Sep 13 | 1,740 |
| Sep 14 | 1,720 |
Series: Active users. 14 points from Sep 1 to Sep 14. Use the arrow keys, Home and End to read values.
Show sourceexamples/area-chart/active-users.tsx
"use client";
import { AreaChart } from "@dethink/components";
const values = [
1_210,
1_260,
1_190,
1_320,
1_410,
1_380,
null,
null,
1_520,
1_610,
1_580,
1_690,
1_740,
1_720,
];
const data = values.map((users, i) => ({ day: `Sep ${i + 1}`, users }));
export function AreaChartActiveUsers() {
return (
<div className="w-full">
<h3 id="active-users-heading" className="mb-3 text-sm font-medium">
Daily active users
</h3>
<AreaChart
aria-labelledby="active-users-heading"
data={data}
index="day"
indexLabel="Day"
height={200}
curve="linear"
series={[{ key: "users", label: "Active users", color: "chart-3" }]}
/>
</div>
);
}| Prop | What it does | Default |
|---|---|---|
dataArray<Record<string, unknown>> | One record per x position, in order. Non-numeric values leave a gap. | Not set |
indexstring | Record key holding the x value, such as "month". | Not set |
seriesArray<{ key, label, color? }> | Areas on one y-axis. color is a palette slot or CSS color; it defaults to the slot for the series' position, so filtering never repaints survivors. | Not set |
stackedboolean | Stack series so the top edge reads as the total. Positive values stack up, negatives down, and a missing value stacks as zero. Tooltip, readout and table keep each series' own value. | false |
curve"monotone" | "linear" | "step" | Monotone never overshoots the data. | "monotone" |
heightnumber | string | Plot height. Width always fills the container. | 288 |
yDomain / includeZero[number, number] / boolean | Fix the value domain, or let the niced data extent drop zero. | — / true |
formatValue / formatOptions(value) => string / Intl.NumberFormatOptions & { locale? } | Formats axis ticks, tooltip, readout and table values. | Compact en-US |
formatIndex / indexLabel(value, index) => string / string | Labels x values and names the table's first column. | String(value) / index |
legendboolean | Toggle buttons that show or hide series; hovering one dims the others. | series.length >= 2 |
hiddenSeries / defaultHiddenSeries / onHiddenSeriesChangestring[] / string[] / (hidden) => void | Controlled or uncontrolled series filtering. The last visible series cannot be hidden. | — / [] / — |
showTable / defaultShowTable / onShowTableChangeboolean / boolean / (show) => void | Swap the plot for a visible data table. The table is always present for assistive technology. | — / false / — |
tableToggleboolean | Render the Table view toggle button. | true |
loading / loadingLabel / emptyLabelboolean / ReactNode / ReactNode | Refetches keep the previous render at reduced opacity. | false / "Loading…" / "No data" |
animateboolean | Draw-in and wash fade on mount. Always off under reduced motion. | true |
AreaChart shares Line Chart's accessible shell. The plot is a single tab stop named by aria-label or aria-labelledby. Arrow keys, Home and End move the crosshair, Escape clears it, and each move is announced through a polite live region listing every series' own value, stacked or not. The data table is always present for assistive technology and can be shown with Table view. Washes are decorative; the legend, tooltip and table name every series, so identity never relies on color alone.