Wide card with a two-line chart of views and unique readers, a date-range select, a legend and a dot on the busiest day. The views color changes between light and dark. Uses recharts 3.8.
"use client"
import { useState } from "react"
import {
CartesianGrid,
Line,
LineChart,
ReferenceDot,
XAxis,
YAxis,
} from "recharts"
import {
Card,
CardAction,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
} from "@/components/ui/chart"
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
// The copy: change text here, not in the markup.
const COPY = {
title: "Traffic overview",
range: "Date range",
} as const
const SAMPLES = [
{ date: "2025-09-01", views: 2100, unique: 1300 },
{ date: "2025-09-04", views: 3700, unique: 2400 },
{ date: "2025-09-08", views: 2900, unique: 2000 },
{ date: "2025-09-10", views: 5300, unique: 3700 },
{ date: "2025-09-13", views: 4400, unique: 3300 },
{ date: "2025-09-15", views: 6800, unique: 5300 },
{ date: "2025-09-17", views: 6100, unique: 4900 },
{ date: "2025-09-18", views: 7600, unique: 5800 },
{ date: "2025-09-22", views: 6300, unique: 5200 },
{ date: "2025-09-26", views: 7200, unique: 5700 },
{ date: "2025-09-29", views: 8000, unique: 6900 },
{ date: "2025-10-01", views: 2600, unique: 1600 },
{ date: "2025-10-04", views: 4500, unique: 3000 },
{ date: "2025-10-08", views: 3500, unique: 2500 },
{ date: "2025-10-10", views: 6400, unique: 4500 },
{ date: "2025-10-13", views: 5400, unique: 4000 },
{ date: "2025-10-15", views: 8300, unique: 6500 },
{ date: "2025-10-17", views: 7400, unique: 6000 },
{ date: "2025-10-18", views: 9240, unique: 7105 },
{ date: "2025-10-22", views: 7700, unique: 6400 },
{ date: "2025-10-26", views: 8800, unique: 7000 },
{ date: "2025-10-29", views: 9800, unique: 8400 },
]
const RANGES = [
{
value: "last-7-days",
label: "Last 7 days",
start: "2025-10-24",
end: "2025-10-30",
description: "Views and unique readers, October 24–30.",
},
{
value: "last-30-days",
label: "Last 30 days",
start: "2025-10-01",
end: "2025-10-30",
description: "Views are up 12.4% on the previous 30 days.",
},
{
value: "last-month",
label: "Last month",
start: "2025-09-01",
end: "2025-09-30",
description: "Views and unique readers through September.",
},
] as const
const CHART_CONFIG = {
views: {
label: "Views",
theme: { light: "var(--chart-5)", dark: "var(--chart-1)" },
},
unique: {
label: "Unique readers",
theme: { light: "var(--chart-2)", dark: "var(--chart-2)" },
},
} satisfies ChartConfig
const DATE_FORMAT = new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
timeZone: "UTC",
})
const formatDate = (value: string) =>
DATE_FORMAT.format(new Date(`${value}T00:00:00Z`))
export default function TrafficOverview() {
const [rangeValue, setRangeValue] = useState<string>("last-30-days")
const range =
RANGES.find((option) => option.value === rangeValue) ?? RANGES[1]
const data = SAMPLES.filter(
(sample) => sample.date >= range.start && sample.date <= range.end
)
// The busiest day in the range gets a dot.
const peak = data.reduce(
(best, sample) => (sample.views > best.views ? sample : best),
data[0]
)
return (
<Card className="mx-auto w-full max-w-3xl">
<CardHeader>
<CardTitle>
<h2>{COPY.title}</h2>
</CardTitle>
<CardDescription>{range.description}</CardDescription>
<CardAction>
<Select
items={RANGES}
value={rangeValue}
onValueChange={(next) => {
if (next) setRangeValue(next)
}}
>
<SelectTrigger size="sm" aria-label={COPY.range}>
<SelectValue />
</SelectTrigger>
<SelectContent align="end">
<SelectGroup>
{RANGES.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</CardAction>
</CardHeader>
<CardContent>
<ChartContainer config={CHART_CONFIG} className="h-80 w-full">
<LineChart accessibilityLayer data={data}>
<CartesianGrid vertical={false} strokeDasharray="3 6" />
{/* Stock ChartContainer's label color misses recharts 3's label layer. */}
<XAxis
tick={{ fill: "var(--muted-foreground)" }}
dataKey="date"
tickLine={false}
axisLine={false}
interval="preserveStartEnd"
tickMargin={10}
tickFormatter={formatDate}
/>
<YAxis hide domain={[0, 10000]} />
<ChartTooltip
content={
<ChartTooltipContent
labelFormatter={(label) => formatDate(String(label))}
/>
}
/>
<ChartLegend content={<ChartLegendContent />} />
<Line
dataKey="views"
type="linear"
stroke="var(--color-views)"
strokeWidth={2}
dot={false}
isAnimationActive={false}
/>
<Line
dataKey="unique"
type="linear"
stroke="var(--color-unique)"
strokeWidth={2}
strokeDasharray="4 6"
dot={false}
activeDot={false}
isAnimationActive={false}
/>
{peak && (
<ReferenceDot
x={peak.date}
y={peak.views}
r={3}
fill="var(--color-views)"
stroke="var(--color-views)"
/>
)}
</LineChart>
</ChartContainer>
</CardContent>
</Card>
)
}
pnpm dlx shadcn add @01orbital/traffic-overviewnpx shadcn add @01orbital/traffic-overviewyarn shadcn add @01orbital/traffic-overviewbunx --bun shadcn add @01orbital/traffic-overviewFree under the MIT license. Add the @01orbital registry to components.json once (install guide), then run the command.
It lands at components/blocks/charts/traffic-overview/index.tsx, as source you own and edit.
import TrafficOverview from "@/components/blocks/charts/traffic-overview"
export default function Example() {
return <TrafficOverview />
}npm: recharts@3.8.0. The CLI installs them for you.
Registry pieces it installs with it:
cardchartselectMonthly Column Bar Chart
Card with a monthly column chart of sessions, the value printed above each rounded bar, and a footer with a trend sentence and icon plus a note. Client component on recharts 3.8.
Weekly Goal Radial Chart
Narrow card with a radial ring showing progress toward a weekly listening goal, minutes in the center, the remaining track visible and a footer note. Client component on recharts 3.8.