ComponentsCharts
Area Chart
Stacked, overlapping or 100% areas in a card, with a crosshair tooltip and a legend of totals.
Stacked
"use client";
import { AreaChart } from "@/components/miyuki-ui/area-chart";
import { createRng } from "@/components/miyuki-ui/lib/prng";
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const titles = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const season = [1.28, 1.22, 1.02, 0.78, 0.62, 0.7, 0.92, 1.0, 0.84, 0.8, 0.96, 1.36];
function channel(seed: string, total: number, growth: number, dip?: [month: number, factor: number]) {
const rng = createRng(seed);
const raw = season.map((s, m) => s * (1 + growth * m) * rng.range(0.94, 1.06) * (dip?.[0] === m ? dip[1] : 1));
const sum = raw.reduce((a, b) => a + b, 0);
const out = raw.map((v) => Math.round((v / sum) * total));
out[11] += total - out.reduce((a, b) => a + b, 0);
return out;
}
const series = [
{ name: "Organic", data: channel("organic", 52140, 0.025) },
{ name: "Referral", data: channel("referral", 26310, 0.04, [8, 0.72]) },
{ name: "Paid", data: channel("paid", 16250, 0.01) },
];
export default function AreaChartDemo() {
return (
<AreaChart
className="w-full max-w-[440px]"
label="Visitors"
value="94,700"
delta={0.082}
labels={months}
titles={titles.map((t) => `${t} 2026`)}
series={series}
/>
);
}Overlapping
"use client";
import { AreaChart } from "@/components/miyuki-ui/area-chart";
import { createRng } from "@/components/miyuki-ui/lib/prng";
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const season = [1.3, 1.24, 1.0, 0.76, 0.64, 0.74, 0.98, 1.04, 0.86, 0.78, 0.94, 1.4];
function channel(seed: string, total: number, growth: number) {
const rng = createRng(seed);
const raw = season.map((s, m) => s * (1 + growth * m) * rng.range(0.93, 1.07));
const sum = raw.reduce((a, b) => a + b, 0);
const out = raw.map((v) => Math.round((v / sum) * total));
out[11] += total - out.reduce((a, b) => a + b, 0);
return out;
}
const series = [
{ name: "Phone app", data: channel("app", 98420, 0.02) },
{ name: "Station screens", data: channel("screens", 71260, 0.045) },
{ name: "Website", data: channel("web", 44700, -0.01) },
];
export default function AreaChartOverlapDemo() {
return (
<AreaChart
className="w-full max-w-[440px]"
mode="overlap"
label="Forecast checks"
value="214,380"
delta={0.056}
labels={months}
series={series}
/>
);
}Percent
"use client";
import { AreaChart } from "@/components/miyuki-ui/area-chart";
import { formatPercent } from "@/components/miyuki-ui/lib/chart";
import { createRng } from "@/components/miyuki-ui/lib/prng";
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const shape = {
organic: [1.0, 1.0, 1.02, 1.05, 1.08, 1.1, 1.06, 1.04, 0.98, 0.92, 0.9, 0.96],
referral: [1.0, 1.02, 1.0, 0.98, 0.96, 0.98, 1.04, 1.06, 0.8, 1.02, 1.04, 1.0],
paid: [0.9, 0.86, 0.8, 0.74, 0.7, 0.72, 0.8, 0.86, 1.1, 1.36, 1.5, 1.2],
};
function channel(seed: string, base: number, factors: number[]) {
const rng = createRng(seed);
return factors.map((f) => Math.round(base * f * rng.range(0.95, 1.05)));
}
const series = [
{ name: "Organic", data: channel("organic", 4340, shape.organic) },
{ name: "Referral", data: channel("referral", 2190, shape.referral) },
{ name: "Paid", data: channel("paid", 1350, shape.paid) },
];
const sum = (data: number[]) => data.reduce((a, b) => a + b, 0);
const organicShare = sum(series[0].data) / series.reduce((total, s) => total + sum(s.data), 0);
export default function AreaChartPercentDemo() {
return (
<AreaChart
className="w-full max-w-[440px]"
mode="percent"
label="Visitors by channel"
value={formatPercent(organicShare)}
caption="organic"
labels={months}
series={series}
/>
);
}Install
npx shadcn@latest add @miyuki-ui/area-chartThis needs the @miyuki-ui registry in your components.json. See Using the shadcn CLI.
Props
AreaChart
Prop
Type