- Charts
- Line Chart
- Bar Chart
- Area Chart
- Scatter Chart
- Pie Chart
- Donut Chart
- Dot Plot Chart
- Lollipop Chart
- Dumbbell Chart
- Slope Chart
- Range Chart
- Proportion Bar
- Histogram Chart
- Box Plot Chart
- Violin Chart
- Polar Chart
- Parallel Coordinates
- SPLOM Chart
- Parcats Chart
- Candlestick Chart
- OHLC Chart
- Waterfall Chart
- Funnel Chart
- Heatmap Chart
- Contour Chart
- Density Chart
- Ternary Chart
- Radar Chart
- Treemap Chart
- Sunburst Chart
- Sankey Chart
- Gauge Chart
- Radial Progress Chart
- Bullet Chart
- Icicle Chart
- Network Graph
- Dendrogram
- Choropleth Chart
"use client"
import { BarChart } from "@/components/ui/charts"
// Page view data - 30 days of traffic
const chartData = [
186, 205, 237, 173, 209, 214, 278, 312, 245, 289, 234, 276, 198, 267, 289,
312, 256, 234, 289, 267, 298, 312, 278, 256, 234, 267, 289, 312, 334, 356,
].map((value, i) => ({
label: `${i + 1}`,
value,
}))
export function BarChartDemo() {
return (
<BarChart
data={chartData}
showGrid
showTooltip
barRadius={0}
aspectRatio={2}
/>
)
}
About
The Bar Chart component is built on top of Recharts and provides a simple, declarative way to create bar charts. It supports various configurations including:
- Vertical and horizontal orientations
- Single and multiple data series
- Stacked and grouped bar layouts
- Custom tooltips with theme-aware styling
- Responsive design that adapts to container width
Emphasis and annotation
activeLabel (or activeIndex) keeps one bar vivid and drops the rest to
mutedColor; pattern / mutedPattern give either group a texture. A
referenceLine takes a literal value or a statistic ("avg", "min",
"max").
Nothing about the annotation look is fixed. labelVariant picks between
plain (text only — the default, since a filled chip floating over a plot
reads as a stuck tooltip), pill and flag; activeTickVariant picks
plain, pill or underline. Set them per chart, set them once on a
ChartThemeProvider, or pass your own renderer to label and skip the
built-ins entirely.
<BarChart
data={data}
activeLabel="May"
referenceLine={{ value: "avg", label: "Average", labelVariant: "flag" }}
activeTickVariant="pill"
/>"use client"
import {
BarChart,
ChartThemeProvider,
} from "@/components/ui/charts"
const chartData = [
{ label: "Jan", value: 5120 },
{ label: "Feb", value: 6840 },
{ label: "Mar", value: 5960 },
{ label: "Apr", value: 9980 },
{ label: "May", value: 9467 },
{ label: "Jun", value: 8210 },
{ label: "Jul", value: 7340 },
]
const currency = (value: number) => `$${value.toLocaleString("en-US")}`
export function BarChartEmphasisDemo() {
return (
<ChartThemeProvider preset="dashboard">
<BarChart
className="max-w-md"
data={chartData}
activeLabel="May"
showActiveValue
showGrid={false}
showYAxis={false}
aspectRatio={1.6}
valueFormatter={currency}
referenceLine={{ value: "avg", label: "Average" }}
/>
</ChartThemeProvider>
)
}
Mobile, zoom and overflow
Every category chart measures its own box, not the viewport, so a chart
in a narrow card behaves like a chart on a phone. aspectRatio and
legendPosition take a value per container-width breakpoint, minHeight
stops a phone-width plot collapsing to a strip, and the category ticks thin
themselves out until the labels stop colliding.
When the categories outnumber the pixels there are only two honest options,
and overflow picks between them: compress squeezes them in, scroll
gives each one minCategorySize and lets the plot overflow — sideways for
vertical bars, downwards for horizontal ones.
Zoom works from every input: drag to box-select, ⌘/ctrl + wheel (or
wheelZoom for a bare wheel), two-finger pinch, one-finger pan once zoomed,
double-tap to reset, plus the +/−/reset buttons. It turns itself off while
the plot is scrolling, since scroll already owns that axis.
Scroll — each bar keeps a floor width and the plot overflows sideways.
Zoom — drag to select, pinch or ⌘-scroll to scale, double-click to reset.
"use client"
import { BarChart } from "@/components/ui/charts"
// 48 days is more categories than a phone has pixels — compressing them
// would leave bars thinner than the gaps between them.
const chartData = Array.from({ length: 48 }, (_, i) => ({
label: `D${i + 1}`,
value: 220 + Math.round(90 * Math.sin(i / 2.3) + 60 * Math.cos(i / 5)),
}))
export function BarChartScrollDemo() {
return (
<div className="w-full space-y-8">
<div className="space-y-2">
<p className="text-muted-foreground text-sm">
<span className="text-foreground font-medium">Scroll</span> — each bar
keeps a floor width and the plot overflows sideways.
</p>
<BarChart
data={chartData}
overflow="scroll"
minCategorySize={34}
aspectRatio={{ base: 1.3, md: 2.6 }}
/>
</div>
<div className="space-y-2">
<p className="text-muted-foreground text-sm">
<span className="text-foreground font-medium">Zoom</span> — drag to
select, pinch or ⌘-scroll to scale, double-click to reset.
</p>
<BarChart
data={chartData}
aspectRatio={{ base: 1.3, md: 2.6 }}
wheelZoom
/>
</div>
</div>
)
}
| Prop | Type | Default | Description |
|---|---|---|---|
aspectRatio | number | { base?, sm?, md?, lg?, xl? } | 2 | Width ÷ height, per container width. |
minHeight | number | 180 | Floor on plot height, px. |
overflow | "compress" | "scroll" | "compress" | What to do when categories outnumber pixels. |
minCategorySize | number | 32 | Room one category needs before scroll overflows. |
zoomable | boolean | true | Drag / pinch / wheel zoom. |
wheelZoom | boolean | false | Let a bare wheel zoom, not just ⌘/ctrl + wheel. |
showZoomControls | boolean | true | The +/−/reset buttons. |
xAxisInterval | number | "preserveStartEnd" | auto | Ticks skipped between labels. |
Installation
pnpm dlx shadcn@latest add https://ui.simplifyingai.com/r/bar-chart.json
Usage
import { BarChart } from "@/components/ui/charts"
const data = [
{ label: "Jan", value: 4000 },
{ label: "Feb", value: 3000 },
{ label: "Mar", value: 5000 },
]
export function MyChart() {
return <BarChart data={data} />
}Examples
Default
The default bar chart displays categorical data with sharp-edged bars, horizontal grid lines, and an interactive tooltip on hover.
"use client"
import { BarChart } from "@/components/ui/charts"
// Page view data - 30 days of traffic
const chartData = [
186, 205, 237, 173, 209, 214, 278, 312, 245, 289, 234, 276, 198, 267, 289,
312, 256, 234, 289, 267, 298, 312, 278, 256, 234, 267, 289, 312, 334, 356,
].map((value, i) => ({
label: `${i + 1}`,
value,
}))
export function BarChartDemo() {
return (
<BarChart
data={chartData}
showGrid
showTooltip
barRadius={0}
aspectRatio={2}
/>
)
}
With Labels
Use the showLabel prop to display values directly on top of each bar. This is useful when you want to show exact values without requiring users to hover.
"use client"
import {
Bar,
BarChart,
CartesianGrid,
LabelList,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts"
const chartData = [
{ month: "January", desktop: 186 },
{ month: "February", desktop: 305 },
{ month: "March", desktop: 237 },
{ month: "April", desktop: 73 },
{ month: "May", desktop: 209 },
{ month: "June", desktop: 214 },
]
export function BarChartLabelDemo() {
return (
<div className="w-full max-w-md">
<ResponsiveContainer width="100%" aspect={1.5}>
<BarChart
data={chartData}
margin={{
top: 30,
right: 10,
left: 10,
bottom: 40,
}}
>
<CartesianGrid
strokeDasharray="3 3"
vertical={false}
stroke="hsl(var(--border))"
strokeOpacity={0.5}
/>
<XAxis
dataKey="month"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickFormatter={(value) => value.slice(0, 3)}
tickMargin={8}
/>
<YAxis hide />
<Tooltip
cursor={{ fill: "hsl(var(--muted-foreground))", opacity: 0.08 }}
content={({ active, payload, label }) => {
if (!active || !payload?.length) return null
return (
<div className="bg-background rounded-lg border px-3 py-2 shadow-lg">
<p className="text-foreground text-sm font-medium">{label}</p>
<p className="text-sm" style={{ color: "#2563eb" }}>
Desktop: {payload[0]?.value}
</p>
</div>
)
}}
/>
<Bar dataKey="desktop" fill="#2563eb" radius={8}>
<LabelList
dataKey="desktop"
position="top"
offset={12}
className="fill-foreground"
fontSize={12}
/>
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
)
}
<BarChart data={data} showLabel labelPosition="top" />Horizontal
Horizontal bar charts are ideal for displaying data with long category names or when you have many categories. Use layout="vertical" to switch orientation (Recharts naming convention).
"use client"
import {
Bar,
BarChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts"
const chartData = [
{ month: "January", desktop: 186 },
{ month: "February", desktop: 305 },
{ month: "March", desktop: 237 },
{ month: "April", desktop: 73 },
{ month: "May", desktop: 209 },
{ month: "June", desktop: 214 },
]
export function BarChartHorizontalDemo() {
return (
<div className="w-full max-w-md">
<ResponsiveContainer width="100%" aspect={1.5}>
<BarChart
data={chartData}
layout="vertical"
margin={{
top: 10,
right: 10,
left: 10,
bottom: 10,
}}
>
<CartesianGrid
strokeDasharray="3 3"
horizontal={false}
stroke="hsl(var(--border))"
strokeOpacity={0.5}
/>
<YAxis
dataKey="month"
type="category"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickFormatter={(value) => value.slice(0, 3)}
width={40}
/>
<XAxis
type="number"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
/>
<Tooltip
cursor={{ fill: "hsl(var(--muted-foreground))", opacity: 0.08 }}
content={({ active, payload, label }) => {
if (!active || !payload?.length) return null
return (
<div className="bg-background rounded-lg border px-3 py-2 shadow-lg">
<p className="text-foreground text-sm font-medium">{label}</p>
<p className="text-sm" style={{ color: "#2563eb" }}>
Desktop: {payload[0]?.value}
</p>
</div>
)
}}
/>
<Bar dataKey="desktop" fill="#2563eb" radius={4} />
</BarChart>
</ResponsiveContainer>
</div>
)
}
<BarChart data={data} layout="vertical" />Multiple / Grouped
Display multiple data series side-by-side to compare different metrics across categories. Use the MultiBarChart component with a series configuration.
"use client"
import {
Bar,
BarChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts"
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
]
export function BarChartMultipleDemo() {
return (
<div className="w-full max-w-md">
<ResponsiveContainer width="100%" aspect={1.5}>
<BarChart
data={chartData}
margin={{
top: 10,
right: 10,
left: 10,
bottom: 40,
}}
>
<CartesianGrid
strokeDasharray="3 3"
vertical={false}
stroke="hsl(var(--border))"
strokeOpacity={0.5}
/>
<XAxis
dataKey="month"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickFormatter={(value) => value.slice(0, 3)}
tickMargin={8}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
width={36}
/>
<Tooltip
cursor={{ fill: "hsl(var(--muted-foreground))", opacity: 0.08 }}
content={({ active, payload, label }) => {
if (!active || !payload?.length) return null
return (
<div className="bg-background rounded-lg border px-3 py-2 shadow-lg">
<p className="text-foreground mb-1 text-sm font-medium">
{label}
</p>
{payload.map((entry, index) => (
<div
key={index}
className="flex items-center gap-2 text-sm"
>
<div
className="size-2 rounded-full"
style={{ backgroundColor: entry.color }}
/>
<span className="text-muted-foreground">
{entry.name}:
</span>
<span style={{ color: entry.color }}>{entry.value}</span>
</div>
))}
</div>
)
}}
/>
<Bar dataKey="desktop" fill="#2563eb" radius={4} />
<Bar dataKey="mobile" fill="#60a5fa" radius={4} />
</BarChart>
</ResponsiveContainer>
{/* Legend */}
<div className="mt-4 flex flex-wrap items-center justify-center gap-4">
<div className="flex items-center gap-2 text-sm">
<div className="size-3 rounded-sm bg-[#2563eb]" />
<span className="text-muted-foreground">Desktop</span>
</div>
<div className="flex items-center gap-2 text-sm">
<div className="size-3 rounded-sm bg-[#60a5fa]" />
<span className="text-muted-foreground">Mobile</span>
</div>
</div>
</div>
)
}
import { MultiBarChart } from "@/components/ui/charts"
const data = [
{ label: "Jan", desktop: 186, mobile: 80 },
{ label: "Feb", desktop: 305, mobile: 200 },
]
const series = [
{ name: "Desktop", dataKey: "desktop", color: "#2563eb" },
{ name: "Mobile", dataKey: "mobile", color: "#60a5fa" },
]
<MultiBarChart data={data} series={series} />Stacked
Stacked bar charts show the cumulative total of multiple series. Add a stackId to each series to group them into stacks.
"use client"
import {
Bar,
BarChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts"
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
]
export function BarChartStackedDemo() {
return (
<div className="w-full max-w-md">
<ResponsiveContainer width="100%" aspect={1.5}>
<BarChart
data={chartData}
margin={{
top: 10,
right: 10,
left: 10,
bottom: 40,
}}
>
<CartesianGrid
strokeDasharray="3 3"
vertical={false}
stroke="hsl(var(--border))"
strokeOpacity={0.5}
/>
<XAxis
dataKey="month"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickFormatter={(value) => value.slice(0, 3)}
tickMargin={8}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
width={36}
/>
<Tooltip
cursor={{ fill: "hsl(var(--muted-foreground))", opacity: 0.08 }}
content={({ active, payload, label }) => {
if (!active || !payload?.length) return null
const total = payload.reduce(
(sum, entry) => sum + (entry.value as number),
0
)
return (
<div className="bg-background rounded-lg border px-3 py-2 shadow-lg">
<p className="text-foreground mb-1 text-sm font-medium">
{label}
</p>
{payload.map((entry, index) => (
<div
key={index}
className="flex items-center gap-2 text-sm"
>
<div
className="size-2 rounded-full"
style={{ backgroundColor: entry.color }}
/>
<span className="text-muted-foreground">
{entry.name}:
</span>
<span style={{ color: entry.color }}>{entry.value}</span>
</div>
))}
<div className="text-muted-foreground mt-1 border-t pt-1 text-sm">
Total: {total}
</div>
</div>
)
}}
/>
<Bar
dataKey="desktop"
fill="#2563eb"
stackId="stack"
radius={[0, 0, 4, 4]}
/>
<Bar
dataKey="mobile"
fill="#60a5fa"
stackId="stack"
radius={[4, 4, 0, 0]}
/>
</BarChart>
</ResponsiveContainer>
{/* Legend */}
<div className="mt-4 flex flex-wrap items-center justify-center gap-4">
<div className="flex items-center gap-2 text-sm">
<div className="size-3 rounded-sm bg-[#2563eb]" />
<span className="text-muted-foreground">Desktop</span>
</div>
<div className="flex items-center gap-2 text-sm">
<div className="size-3 rounded-sm bg-[#60a5fa]" />
<span className="text-muted-foreground">Mobile</span>
</div>
</div>
</div>
)
}
const series = [
{ name: "Desktop", dataKey: "desktop", color: "#2563eb", stackId: "stack" },
{ name: "Mobile", dataKey: "mobile", color: "#60a5fa", stackId: "stack" },
]
<MultiBarChart data={data} series={series} />Negative Values
Bar charts can display both positive and negative values. Bars automatically extend above or below the zero reference line.
"use client"
import {
Bar,
BarChart,
CartesianGrid,
Cell,
ReferenceLine,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts"
const chartData = [
{ month: "January", value: 186 },
{ month: "February", value: -125 },
{ month: "March", value: 237 },
{ month: "April", value: -73 },
{ month: "May", value: 209 },
{ month: "June", value: -156 },
]
export function BarChartNegativeDemo() {
return (
<div className="w-full max-w-md">
<ResponsiveContainer width="100%" aspect={1.5}>
<BarChart
data={chartData}
margin={{
top: 10,
right: 10,
left: 10,
bottom: 40,
}}
>
<CartesianGrid
strokeDasharray="3 3"
vertical={false}
stroke="hsl(var(--border))"
strokeOpacity={0.5}
/>
<XAxis
dataKey="month"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickFormatter={(value) => value.slice(0, 3)}
tickMargin={8}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
width={40}
/>
<ReferenceLine y={0} stroke="hsl(var(--border))" strokeWidth={1} />
<Tooltip
cursor={{ fill: "hsl(var(--muted-foreground))", opacity: 0.08 }}
content={({ active, payload, label }) => {
if (!active || !payload?.length) return null
const value = payload[0]?.value as number
return (
<div className="bg-background rounded-lg border px-3 py-2 shadow-lg">
<p className="text-foreground text-sm font-medium">{label}</p>
<p
className="text-sm"
style={{ color: value >= 0 ? "#2563eb" : "#60a5fa" }}
>
Value: {value}
</p>
</div>
)
}}
/>
<Bar dataKey="value" radius={4}>
{chartData.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={entry.value >= 0 ? "#2563eb" : "#60a5fa"}
/>
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
)
}
Interactive
Create interactive charts with state-controlled data series. Users can toggle between different views of the data.
"use client"
import * as React from "react"
import {
Bar,
BarChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts"
const chartData = [
{ date: "2024-04-01", desktop: 222, mobile: 150 },
{ date: "2024-04-02", desktop: 97, mobile: 180 },
{ date: "2024-04-03", desktop: 167, mobile: 120 },
{ date: "2024-04-04", desktop: 242, mobile: 260 },
{ date: "2024-04-05", desktop: 373, mobile: 290 },
{ date: "2024-04-06", desktop: 301, mobile: 340 },
{ date: "2024-04-07", desktop: 245, mobile: 180 },
{ date: "2024-04-08", desktop: 409, mobile: 320 },
{ date: "2024-04-09", desktop: 59, mobile: 110 },
{ date: "2024-04-10", desktop: 261, mobile: 190 },
{ date: "2024-04-11", desktop: 327, mobile: 350 },
{ date: "2024-04-12", desktop: 292, mobile: 210 },
{ date: "2024-04-13", desktop: 342, mobile: 380 },
{ date: "2024-04-14", desktop: 137, mobile: 220 },
{ date: "2024-04-15", desktop: 120, mobile: 170 },
{ date: "2024-04-16", desktop: 138, mobile: 190 },
{ date: "2024-04-17", desktop: 446, mobile: 360 },
{ date: "2024-04-18", desktop: 364, mobile: 410 },
{ date: "2024-04-19", desktop: 243, mobile: 180 },
{ date: "2024-04-20", desktop: 89, mobile: 150 },
]
export function BarChartInteractiveDemo() {
const [activeChart, setActiveChart] = React.useState<"desktop" | "mobile">(
"desktop"
)
const total = React.useMemo(
() => ({
desktop: chartData.reduce((acc, curr) => acc + curr.desktop, 0),
mobile: chartData.reduce((acc, curr) => acc + curr.mobile, 0),
}),
[]
)
return (
<div className="w-full">
{/* Header with toggles */}
<div className="flex">
<button
className={`flex flex-1 flex-col justify-center gap-1 border-b px-6 py-4 text-left transition-colors even:border-l sm:border-l sm:px-8 sm:py-6 ${
activeChart === "desktop" ? "bg-muted/50" : "hover:bg-muted/30"
}`}
onClick={() => setActiveChart("desktop")}
>
<span className="text-muted-foreground text-xs">Desktop</span>
<span className="text-foreground text-lg leading-none font-bold sm:text-3xl">
{total.desktop.toLocaleString()}
</span>
</button>
<button
className={`flex flex-1 flex-col justify-center gap-1 border-b px-6 py-4 text-left transition-colors even:border-l sm:border-l sm:px-8 sm:py-6 ${
activeChart === "mobile" ? "bg-muted/50" : "hover:bg-muted/30"
}`}
onClick={() => setActiveChart("mobile")}
>
<span className="text-muted-foreground text-xs">Mobile</span>
<span className="text-foreground text-lg leading-none font-bold sm:text-3xl">
{total.mobile.toLocaleString()}
</span>
</button>
</div>
{/* Chart */}
<div className="px-2 pt-4 sm:px-6 sm:pt-6">
<ResponsiveContainer width="100%" aspect={2.5}>
<BarChart
data={chartData}
margin={{
top: 10,
right: 10,
left: 10,
bottom: 40,
}}
>
<CartesianGrid
strokeDasharray="3 3"
vertical={false}
stroke="hsl(var(--border))"
strokeOpacity={0.5}
/>
<XAxis
dataKey="date"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickFormatter={(value) => {
const date = new Date(value)
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
})
}}
tickMargin={8}
minTickGap={32}
/>
<YAxis hide />
<Tooltip
cursor={{ fill: "hsl(var(--muted-foreground))", opacity: 0.08 }}
content={({ active, payload, label }) => {
if (!active || !payload?.length) return null
const date = new Date(label)
return (
<div className="bg-background rounded-lg border px-3 py-2 shadow-lg">
<p className="text-foreground text-sm font-medium">
{date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}
</p>
<p
className="text-sm"
style={{
color:
activeChart === "desktop" ? "#2563eb" : "#60a5fa",
}}
>
{activeChart === "desktop" ? "Desktop" : "Mobile"}:{" "}
{payload[0]?.value}
</p>
</div>
)
}}
/>
<Bar
dataKey={activeChart}
fill={activeChart === "desktop" ? "#2563eb" : "#60a5fa"}
radius={[4, 4, 0, 0]}
/>
</BarChart>
</ResponsiveContainer>
</div>
</div>
)
}
Mixed (Bar + Line)
Combine bars with a line chart to show both absolute values and trends. Uses Recharts' ComposedChart component.
"use client"
import {
Bar,
CartesianGrid,
ComposedChart,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts"
const chartData = [
{ month: "January", desktop: 186, trend: 160 },
{ month: "February", desktop: 305, trend: 220 },
{ month: "March", desktop: 237, trend: 250 },
{ month: "April", desktop: 73, trend: 180 },
{ month: "May", desktop: 209, trend: 200 },
{ month: "June", desktop: 214, trend: 230 },
]
export function BarChartMixedDemo() {
return (
<div className="w-full max-w-md">
<ResponsiveContainer width="100%" aspect={1.5}>
<ComposedChart
data={chartData}
margin={{
top: 10,
right: 10,
left: 10,
bottom: 40,
}}
>
<CartesianGrid
strokeDasharray="3 3"
vertical={false}
stroke="hsl(var(--border))"
strokeOpacity={0.5}
/>
<XAxis
dataKey="month"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickFormatter={(value) => value.slice(0, 3)}
tickMargin={8}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
width={36}
/>
<Tooltip
cursor={{ fill: "hsl(var(--muted-foreground))", opacity: 0.08 }}
content={({ active, payload, label }) => {
if (!active || !payload?.length) return null
return (
<div className="bg-background rounded-lg border px-3 py-2 shadow-lg">
<p className="text-foreground mb-1 text-sm font-medium">
{label}
</p>
{payload.map((entry, index) => (
<div
key={index}
className="flex items-center gap-2 text-sm"
>
<div
className="size-2 rounded-full"
style={{ backgroundColor: entry.color }}
/>
<span className="text-muted-foreground">
{entry.name}:
</span>
<span style={{ color: entry.color }}>{entry.value}</span>
</div>
))}
</div>
)
}}
/>
<Bar dataKey="desktop" name="Desktop" fill="#2563eb" radius={4} />
<Line
type="monotone"
dataKey="trend"
name="Trend"
stroke="#60a5fa"
strokeWidth={2}
dot={{ r: 4, fill: "#60a5fa" }}
activeDot={{ r: 6, fill: "#60a5fa" }}
/>
</ComposedChart>
</ResponsiveContainer>
{/* Legend */}
<div className="mt-4 flex flex-wrap items-center justify-center gap-4">
<div className="flex items-center gap-2 text-sm">
<div className="size-3 rounded-sm bg-[#2563eb]" />
<span className="text-muted-foreground">Desktop</span>
</div>
<div className="flex items-center gap-2 text-sm">
<div className="h-0.5 w-3 rounded-full bg-[#60a5fa]" />
<span className="text-muted-foreground">Trend</span>
</div>
</div>
</div>
)
}
Active Bar
Highlight a specific bar using a custom shape with a dashed border effect. Useful for emphasizing a selected or current data point.
"use client"
import {
Bar,
BarChart,
Rectangle,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts"
const chartData = [
{ month: "January", sales: 187, fill: "#93c5fd" },
{ month: "February", sales: 200, fill: "#3b82f6" },
{ month: "March", sales: 275, fill: "#2563eb" },
{ month: "April", sales: 173, fill: "#1d4ed8" },
{ month: "May", sales: 209, fill: "#1e40af" },
]
// Index of the active (highlighted) bar
const ACTIVE_INDEX = 2
interface BarShapeProps {
x?: number
y?: number
width?: number
height?: number
fill?: string
index?: number
payload?: { fill: string }
radius?: number | [number, number, number, number]
[key: string]: unknown
}
export function BarChartActiveDemo() {
return (
<div className="w-full max-w-md">
<ResponsiveContainer width="100%" aspect={1.5}>
<BarChart
data={chartData}
margin={{
top: 10,
right: 10,
left: 10,
bottom: 40,
}}
>
<XAxis
dataKey="month"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12 }}
tickFormatter={(value) => value.slice(0, 3)}
tickMargin={10}
/>
<YAxis hide />
<Tooltip
cursor={false}
content={({ active, payload }) => {
if (!active || !payload?.length) return null
const data = payload[0]
return (
<div className="bg-background flex items-center gap-2 rounded-lg border px-3 py-2 shadow-lg">
<div
className="size-3 rounded-sm"
style={{ backgroundColor: data.payload?.fill }}
/>
<span className="text-muted-foreground text-sm">Sales</span>
<span className="text-foreground text-sm font-medium">
{data.value}
</span>
</div>
)
}}
/>
<Bar
dataKey="sales"
radius={12}
shape={(props: unknown) => {
const { index, fill, ...rest } = props as BarShapeProps
return index === ACTIVE_INDEX ? (
<Rectangle
{...rest}
fill={fill}
fillOpacity={0.6}
stroke={fill}
strokeWidth={2}
strokeDasharray="4 4"
/>
) : (
<Rectangle {...rest} fill={fill} />
)
}}
/>
</BarChart>
</ResponsiveContainer>
</div>
)
}
import { Bar, BarChart, Rectangle } from "recharts"
const ACTIVE_INDEX = 2
<Bar
dataKey="sales"
radius={12}
shape={({ index, fill, ...props }) =>
index === ACTIVE_INDEX ? (
<Rectangle
{...props}
fill={fill}
fillOpacity={0.6}
stroke={fill}
strokeWidth={2}
strokeDasharray="4 4"
/>
) : (
<Rectangle {...props} fill={fill} />
)
}
/>API Reference
BarChart
| Prop | Type | Default | Description |
|---|---|---|---|
data | BarChartDataPoint[] | required | Array of data points to display |
color | string | "#2563eb" | Fill color for bars |
className | string | - | Additional CSS classes |
showGrid | boolean | true | Show horizontal grid lines |
showTooltip | boolean | true | Enable tooltip on hover |
showLabel | boolean | false | Show value labels on bars |
labelPosition | "top" | "center" | "bottom" | "inside" | "top" | Position of value labels |
valueFormatter | (value: number) => string | - | Format displayed values |
labelFormatter | (label: string) => string | - | Format category labels |
layout | "vertical" | "horizontal" | "horizontal" | Bar orientation (vertical = horizontal bars) |
barRadius | number | 4 | Corner radius of bars |
aspectRatio | number | 2 | Width to height ratio |
yAxisWidth | number | 48 | Width reserved for Y-axis labels |
MultiBarChart
| Prop | Type | Default | Description |
|---|---|---|---|
data | MultiBarChartDataPoint[] | required | Array of data points |
series | MultiBarChartSeries[] | required | Configuration for each data series |
className | string | - | Additional CSS classes |
showGrid | boolean | true | Show horizontal grid lines |
showTooltip | boolean | true | Enable tooltip on hover |
showLegend | boolean | true | Show legend below chart |
valueFormatter | (value: number) => string | - | Format displayed values |
labelFormatter | (label: string) => string | - | Format category labels |
layout | "vertical" | "horizontal" | "horizontal" | Bar orientation |
barRadius | number | 4 | Corner radius of bars |
aspectRatio | number | 2 | Width to height ratio |
yAxisWidth | number | 48 | Width reserved for Y-axis labels |
Data Types
// Single series data point
interface BarChartDataPoint {
label: string // Category name (e.g., "January")
value: number // Numeric value
fill?: string // Optional: override color for this bar
}
// Multiple series data point
interface MultiBarChartDataPoint {
label: string // Category name
[key: string]: string | number // Dynamic keys for each series
}
// Series configuration
interface MultiBarChartSeries {
name: string // Display name in tooltip/legend
dataKey: string // Key in data object
color: string // Bar fill color
stackId?: string // Optional: group into stack
}Customization
Custom Colors
Pass a color prop or use per-bar colors via the fill property in data:
const data = [
{ label: "Success", value: 80, fill: "#22c55e" },
{ label: "Warning", value: 15, fill: "#eab308" },
{ label: "Error", value: 5, fill: "#ef4444" },
]
<BarChart data={data} />Custom Tooltip
For advanced tooltip customization, use the Recharts components directly:
import { Bar, BarChart, Tooltip } from "recharts"
;<BarChart data={data}>
<Tooltip
content={({ active, payload }) => (
<div className="custom-tooltip">{/* Your custom content */}</div>
)}
/>
<Bar dataKey="value" />
</BarChart>Accessibility
- Charts include proper ARIA labels
- Tooltips are keyboard accessible
- Color schemes maintain sufficient contrast
- Grid lines help users track values across the chart
Notes
- Bar charts are best suited for comparing categorical data across different groups
- Use horizontal layout (
layout="vertical") when dealing with long category labels - Stacked bar charts are ideal for showing part-to-whole relationships over categories
- The
MultiBarChartcomponent automatically handles legend generation and color coordination - For time-series data with many points, consider using a line or area chart instead
- Bar radius can be adjusted for different visual styles (sharp edges vs rounded corners)