
An interactive network bandwidth and throughput area chart with time range select built with TanStack Charts.
components/tanstack-charts/area/area-interactive.tsx
import { areaY, defineChart, lineY } from "@tanstack/charts";
import { d3Curve } from "@tanstack/charts/d3/shape";
import { motion } from "@tanstack/charts/motion";
import { tooltip } from "@tanstack/charts/tooltip";
import { Chart } from "@tanstack/react-charts/core";
import { useMemo, useState } from "react";
import { extent, max } from "d3-array";
import { scaleLinear, scaleTime } from "d3-scale";
import { curveMonotoneX } from "d3-shape";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import type { ChartConfig } from "@/components/ui/chart";
const startTime = new Date(2026, 6, 1, 0, 0, 0).getTime();
const HOUR_MS = 60 * 60 * 1000;
const chartData = Array.from({ length: 72 }, (_, i) => {
const date = new Date(startTime + i * HOUR_MS);
const hour = date.getHours();
const diurnalCycle = Math.sin(((hour - 6) / 24) * 2 * Math.PI) * 0.3 + 0.7;
const microNoise = Math.sin(i * 0.7) * 4 + Math.cos(i * 1.3) * 6;
const periodicSpike = i % 12 === 0 ? 15 : 0;
const desktop = Math.round((250 + microNoise * 10 + periodicSpike * 5) * diurnalCycle + 120);
const mobile = Math.round((180 + microNoise * 8 + periodicSpike * 3) * diurnalCycle + 90);
return {
date,
desktop,
mobile,
};
});
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies ChartConfig;
const formatTooltipDate = (date: unknown) => {
if (!(date instanceof Date)) return "";
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
});
};
type MetricTab = "combined" | "desktop" | "mobile";
const motionRenderer = motion({
initial: "always",
transition: { type: "spring", stiffness: 140, damping: 16, mass: 0.8 },
});
export const Demo = () => {
const [activeTab, setActiveTab] = useState<MetricTab>("combined");
const stats = useMemo(() => {
const totalDesktop = chartData.reduce((acc, curr) => acc + curr.desktop, 0);
const totalMobile = chartData.reduce((acc, curr) => acc + curr.mobile, 0);
return {
combined: Math.round((totalDesktop + totalMobile) / chartData.length),
desktop: Math.round(totalDesktop / chartData.length),
mobile: Math.round(totalMobile / chartData.length),
};
}, []);
const xDomain = useMemo(() => extent(chartData, (d) => d.date) as [Date, Date], []);
const fixedYMax = useMemo(() => {
const peak = max(chartData, (d) => Math.max(d.desktop, d.mobile)) ?? 350;
return Math.ceil(peak / 50) * 50;
}, []);
const definition = useMemo(() => {
const getDesktopY = (d: (typeof chartData)[number]) => {
if (activeTab === "mobile") return d.mobile;
return d.desktop;
};
const getMobileY = (d: (typeof chartData)[number]) => {
if (activeTab === "desktop") return d.desktop;
return d.mobile;
};
const getDesktopColor = () => {
if (activeTab === "mobile") return chartConfig.mobile.color;
return chartConfig.desktop.color;
};
const getMobileColor = () => {
if (activeTab === "desktop") return chartConfig.desktop.color;
return chartConfig.mobile.color;
};
const marks = [
// Desktop Area
areaY(chartData, {
id: "desktop-area",
key: (d) => d.date.toISOString(),
x: (d) => d.date,
y: getDesktopY,
fill: getDesktopColor(),
fillOpacity: 0.35,
curve: d3Curve(curveMonotoneX),
}),
// Desktop Line
lineY(chartData, {
id: "desktop-line",
key: (d) => d.date.toISOString(),
x: (d) => d.date,
y: getDesktopY,
stroke: getDesktopColor(),
strokeWidth: 2,
curve: d3Curve(curveMonotoneX),
points: false,
}),
// Mobile Area
areaY(chartData, {
id: "mobile-area",
key: (d) => d.date.toISOString(),
x: (d) => d.date,
y: getMobileY,
fill: getMobileColor(),
fillOpacity: 0.35,
curve: d3Curve(curveMonotoneX),
}),
// Mobile Line
lineY(chartData, {
id: "mobile-line",
key: (d) => d.date.toISOString(),
x: (d) => d.date,
y: getMobileY,
stroke: getMobileColor(),
strokeWidth: 2,
curve: d3Curve(curveMonotoneX),
points: false,
}),
];
return defineChart(
{
marks,
renderer: motionRenderer,
motion: {
transition: { type: "spring", stiffness: 170, damping: 18, mass: 1 },
},
scales: {
x: {
scale: scaleTime().domain(xDomain),
},
y: {
scale: scaleLinear().domain([0, fixedYMax]).nice(),
grid: true,
},
},
},
{
focus: "group-x",
tooltip: {
use: tooltip,
motion: { type: "spring", stiffness: 170, damping: 18 },
className: "font-sans! shadow-none! capitalize!",
content: (points) => {
const datum = points[0]?.datum as (typeof chartData)[number];
if (!datum) return { rows: [] };
const activeMetrics = activeTab === "combined" ? (["desktop", "mobile"] as const) : [activeTab];
return {
title: formatTooltipDate(points[0].xValue),
rows: activeMetrics.map((key) => ({
label: chartConfig[key].label,
value: `${datum[key].toLocaleString("en-US")} visitors`,
color: chartConfig[key].color,
})),
};
},
},
},
);
}, [activeTab, xDomain, fixedYMax]);
return (
<Card className="gap-0 py-4 sm:py-0">
<CardHeader className="flex flex-col items-stretch border-b p-0! sm:flex-row">
{/* Title Context */}
<div className="flex flex-1 flex-col justify-center gap-1 px-6 pt-4 pb-3 sm:pt-6 sm:pb-6">
<CardTitle className="text-lg">Live Server Throughput</CardTitle>
<CardDescription>
{activeTab === "combined"
? "Interactive visitor traffic across desktop and mobile devices (Last 72 Hours)"
: `Isolated traffic trend for ${chartConfig[activeTab].label}`}
</CardDescription>
</div>
{/* Tab Controls */}
<div className="flex">
{(["combined", "desktop", "mobile"] as const).map((key) => {
const label = key === "combined" ? "Combined Avg" : chartConfig[key].label;
return (
<button
key={key}
data-active={activeTab === key}
className="data-[active=true]:bg-muted/50 hover:bg-muted/30 flex flex-1 flex-col justify-center gap-1 border-t px-4 py-4 text-left transition-colors even:border-l sm:border-t-0 sm:border-l sm:px-6 sm:py-6"
onClick={() => setActiveTab(key)}>
<span className="text-muted-foreground text-xs lg:text-nowrap">{label}</span>
<span className="text-lg leading-none font-bold sm:text-2xl">
{stats[key].toLocaleString("en-US")}
<span className="text-muted-foreground ml-1 text-xs font-normal">/hr</span>
</span>
</button>
);
})}
</div>
</CardHeader>
<CardContent className="px-2 pt-4 sm:p-6 sm:pt-8">
{/* Dynamic Legend */}
<div className="text-muted-foreground mb-4 flex items-center justify-end gap-5 text-xs font-medium">
{(activeTab === "combined" || activeTab === "desktop") && (
<div className="flex items-center gap-2">
<span
className="h-2.5 w-2.5 rounded-full"
style={{ backgroundColor: chartConfig.desktop.color }}
/>
<span>Desktop</span>
</div>
)}
{(activeTab === "combined" || activeTab === "mobile") && (
<div className="flex items-center gap-2">
<span
className="h-2.5 w-2.5 rounded-full"
style={{ backgroundColor: chartConfig.mobile.color }}
/>
<span>Mobile</span>
</div>
)}
</div>
<div className="h-[300px] w-full">
<Chart
definition={definition}
renderer={motionRenderer}
height={300}
ariaLabel={`Live server throughput chart displaying ${activeTab} data`}
/>
</div>
</CardContent>
</Card>
);
};
export default Demo;