beUI Pro pour mobile est disponible : 50 blocs et écrans React Native
Charts
Composable, animated chart components built from small parts, with keyboard support, gliding tooltips, and theme-aware color classes.
Stacked bar chart
stacked-bar-chart.tsxStacked bars over time that grow from the baseline, with a tooltip that glides beside the active bar, reference lines, projected bars, series toggles, and arrow-key navigation.
"use client";
import { useState } from "react";
import { formatCompactValue } from "@/components/charts/chart-series";
import { SeriesToggles } from "@/components/charts/series-toggles";
import {
type ChartSeries,
StackedBarChart,
StackedBarChartAxis,
StackedBarChartPlot,
StackedBarChartReferenceLine,
StackedBarChartTooltip,
type StackedBarDatum,
} from "@/components/charts/stacked-bar-chart";
const SERIES: ChartSeries[] = [
{ id: "web", label: "Web", color: "bg-blue-500" },
{ id: "mobile", label: "Mobile", color: "bg-orange-500" },
{ id: "api", label: "API", color: "bg-teal-500" },
];
const DATA: StackedBarDatum[] = Array.from({ length: 21 }, (_, i) => {
const wave = Math.sin(i / 2.4) * 0.25 + 1 + i * 0.02;
return {
id: `day-${i + 1}`,
label: `Mar ${i + 1}`,
values: {
web: Math.round(4_200 * wave + ((i * 37) % 9) * 120),
mobile: Math.round(3_100 * wave + ((i * 53) % 7) * 140),
api: Math.round(1_800 * wave + ((i * 29) % 5) * 160),
},
};
});
export function StackedBarChartPreview() {
const [visible, setVisible] = useState<ReadonlySet<string>>(
() => new Set(SERIES.map((s) => s.id)),
);
const toggle = (id: string) =>
setVisible((current) => {
const next = new Set(current);
if (next.has(id)) next.delete(id);
else next.add(id);
return next;
});
return (
<div className="mx-auto w-full max-w-3xl px-4 py-10">
<StackedBarChart
data={DATA}
series={SERIES}
visibleSeries={visible}
projectFrom={17}
projectedValue={11_000}
formatValue={formatCompactValue}
label="Requests per day"
className="pt-6"
>
<StackedBarChartPlot className="h-56">
<StackedBarChartReferenceLine value={12_000} label="Target 12k" />
<StackedBarChartTooltip />
</StackedBarChartPlot>
<StackedBarChartAxis
marks={[
{ index: 0, label: "Mar 1" },
{ index: 16, label: "Today", emphasis: true },
{ index: 20, label: "Mar 21" },
]}
/>
</StackedBarChart>
<SeriesToggles
series={SERIES}
visible={visible}
onToggle={toggle}
className="mt-6"
/>
</div>
);
}
Le code source est verrouillé
Obtenez un accès pour installer les composants premium et voir tout le code source.
Ouvrir le tableau de bordInstallation
Segmented meter
segmented-meter.tsxA capacity meter split into colored segments, with a gliding tooltip, a linked legend, selection, and hover state you can share with tables or lists.
Storage
1,425 of 2,000 GB
Select a segment to filter
"use client";
import { useState } from "react";
import {
type MeterSegment,
SegmentedMeter,
SegmentedMeterLegend,
SegmentedMeterTooltip,
SegmentedMeterTrack,
} from "@/components/charts/segmented-meter";
const SEGMENTS: MeterSegment[] = [
{ id: "photos", label: "Photos", value: 612, color: "bg-sky-500" },
{ id: "video", label: "Video", value: 438, color: "bg-orange-500" },
{ id: "docs", label: "Documents", value: 214, color: "bg-emerald-500" },
{ id: "backups", label: "Backups", value: 161, color: "bg-amber-500" },
];
export function SegmentedMeterPreview() {
const [selected, setSelected] = useState<string | null>(null);
const picked = SEGMENTS.find((s) => s.id === selected);
return (
<div className="mx-auto w-full max-w-xl px-4 py-10">
<div className="flex items-baseline justify-between gap-3 text-sm">
<p className="font-medium">Storage</p>
<p className="text-muted-foreground tabular-nums">1,425 of 2,000 GB</p>
</div>
<SegmentedMeter
segments={SEGMENTS}
max={2_000}
selectedId={selected}
onSelect={(id) =>
setSelected((current) => (current === id ? null : id))
}
formatValue={(value) => `${value} GB`}
label="Storage by file type"
>
<SegmentedMeterTrack className="mt-16">
<SegmentedMeterTooltip />
</SegmentedMeterTrack>
<SegmentedMeterLegend trailing="575 GB free" />
</SegmentedMeter>
<p className="mt-6 text-muted-foreground text-sm" aria-live="polite">
{picked
? `Showing ${picked.label.toLowerCase()} only`
: "Select a segment to filter"}
</p>
</div>
);
}
Le code source est verrouillé
Obtenez un accès pour installer les composants premium et voir tout le code source.
Ouvrir le tableau de bordInstallation
Sparkline
sparkline.tsxA compact trend line that draws in from the left and renders correctly before hydration.
- +12%
Signups
2,418
- +4%
Active teams
864
- −9%
Churned
37
- +3%
Avg. response
212 ms
"use client";
import { Sparkline } from "@/components/charts/sparkline";
const METRICS = [
{
label: "Signups",
value: "2,418",
change: "+12%",
stroke: "stroke-blue-500",
values: [12, 14, 13, 17, 16, 19, 22, 21, 24, 27, 26, 30],
},
{
label: "Active teams",
value: "864",
change: "+4%",
stroke: "stroke-teal-500",
values: [40, 41, 43, 42, 44, 45, 44, 46, 47, 47, 48, 49],
},
{
label: "Churned",
value: "37",
change: "−9%",
stroke: "stroke-orange-500",
values: [9, 8, 9, 7, 8, 6, 7, 6, 5, 6, 5, 4],
},
{
label: "Avg. response",
value: "212 ms",
change: "+3%",
stroke: "stroke-lime-500",
values: [20, 22, 19, 23, 21, 24, 22, 25, 23, 24, 26, 25],
},
];
export function SparklinePreview() {
return (
<ul className="mx-auto w-full max-w-md divide-y divide-border px-4 py-8">
{METRICS.map((metric, index) => (
<li key={metric.label} className="flex items-center gap-4 py-4">
<div className="min-w-0 flex-1">
<p className="text-muted-foreground text-sm">{metric.label}</p>
<p className="mt-0.5 font-medium text-xl tabular-nums">
{metric.value}
</p>
</div>
<Sparkline
values={metric.values}
strokeClassName={metric.stroke}
width={96}
height={32}
delay={0.2 + index * 0.1}
label={`${metric.label} trend, ${metric.change}`}
className="h-8 w-24"
/>
<span className="w-12 text-right font-mono text-muted-foreground text-xs">
{metric.change}
</span>
</li>
))}
</ul>
);
}
Le code source est verrouillé
Obtenez un accès pour installer les composants premium et voir tout le code source.
Ouvrir le tableau de bord