Bar Chart
Flexible bar chart supporting grouped, stacked, vertical, and horizontal layouts
Installation
Dependencies
Examples
Custom Tooltip
"use client"
import BarChart, { Bar, BarXAxis, ChartTooltip, Grid } from "@/components/vritti/bar-chart"
const data = [
{ month: "Jan", revenue: 12000, profit: 4500 },
{ month: "Feb", revenue: 15500, profit: 5200 },
{ month: "Mar", revenue: 11000, profit: 3800 },
{ month: "Apr", revenue: 18500, profit: 7100 },
{ month: "May", revenue: 16800, profit: 5400 },
{ month: "Jun", revenue: 21200, profit: 8800 },
]
export function BarChartCustomTooltipDemo() {
return (
<div className="w-full p-4">
<BarChart data={data} xDataKey="month">
<Grid horizontal />
<Bar dataKey="revenue" fill="hsl(217, 91%, 60%)" lineCap={4} />
<Bar dataKey="profit" fill="hsl(280, 87%, 65%)" lineCap={4} />
<BarXAxis />
<ChartTooltip
rows={(point) => [
{
color: "hsl(217, 91%, 60%)",
label: "Revenue",
value: `$${(point.revenue as number)?.toLocaleString()}`,
},
{
color: "hsl(280, 87%, 65%)",
label: "Profit",
value: `$${(point.profit as number)?.toLocaleString()}`,
},
]}
/>
</BarChart>
</div>
)
}
Gradient
"use client"
import BarChart, { Bar, BarXAxis, ChartTooltip, Grid, LinearGradient } from "@/components/vritti/bar-chart"
const data = [
{ month: "Jan", revenue: 12000 },
{ month: "Feb", revenue: 15500 },
{ month: "Mar", revenue: 11000 },
{ month: "Apr", revenue: 18500 },
{ month: "May", revenue: 16800 },
{ month: "Jun", revenue: 21200 },
]
export function BarChartGradientDemo() {
return (
<div className="w-full p-4">
<BarChart data={data} xDataKey="month">
<LinearGradient
from="hsl(217, 91%, 60%)"
id="barGradient"
to="hsl(280, 87%, 65%)"
/>
<Grid horizontal />
<Bar
dataKey="revenue"
fill="url(#barGradient)"
lineCap={4}
stroke="hsl(217, 91%, 60%)"
/>
<BarXAxis />
<ChartTooltip />
</BarChart>
</div>
)
}
Horizontal
"use client"
import BarChart, { Bar, BarYAxis, Grid, ChartTooltip } from "@/components/vritti/bar-chart"
const data = [
{ browser: "Chrome", users: 275 },
{ browser: "Safari", users: 200 },
{ browser: "Firefox", users: 187 },
{ browser: "Edge", users: 173 },
{ browser: "Other", users: 90 },
]
export function BarChartHorizontalDemo() {
return (
<div className="w-full p-4">
<BarChart data={data} xDataKey="browser" orientation="horizontal" aspectRatio="4 / 3" margin={{ left: 80 }}>
<Grid fadeVertical horizontal={false} vertical />
<Bar dataKey="users" fill="hsl(217, 91%, 60%)" lineCap={4} />
<BarYAxis />
<ChartTooltip showCrosshair={false} />
</BarChart>
</div>
)
}
Interactive
"use client"
import BarChart, { Bar, BarXAxis, Grid, ChartTooltip } from "@/components/vritti/bar-chart"
const data = [
{ category: "Electronics", sales: 42500 },
{ category: "Clothing", sales: 31200 },
{ category: "Groceries", sales: 28900 },
{ category: "Furniture", sales: 19400 },
{ category: "Books", sales: 15800 },
{ category: "Sports", sales: 12300 },
{ category: "Toys", sales: 9700 },
]
export function BarChartInteractiveDemo() {
return (
<div className="w-full p-4">
<BarChart data={data} xDataKey="category">
<Grid horizontal />
<Bar
dataKey="sales"
fadedOpacity={0.15}
fill="hsl(217, 91%, 60%)"
lineCap={4}
/>
<BarXAxis />
<ChartTooltip />
</BarChart>
</div>
)
}
Label
"use client"
import BarChart, { Bar, BarXAxis, Grid, ChartTooltip, useChart } from "@/components/vritti/bar-chart"
const data = [
{ product: "Widget A", units: 1240 },
{ product: "Widget B", units: 980 },
{ product: "Widget C", units: 1560 },
{ product: "Widget D", units: 720 },
{ product: "Widget E", units: 1890 },
{ product: "Widget F", units: 1100 },
]
function BarLabels({ dataKey }: { dataKey: string }) {
const { data, barScale, yScale, bandWidth, barXAccessor } = useChart()
if (!barScale || !bandWidth || !barXAccessor) return null
return (
<g>
{data.map((d, i) => {
const value = d[dataKey]
if (typeof value !== "number") return null
const label = barXAccessor(d)
const x = (barScale(label) ?? 0) + bandWidth / 2
const y = (yScale(value) ?? 0) - 10
return (
<text
className="fill-chart-foreground-muted"
dominantBaseline="middle"
fontSize="11"
fontWeight="500"
key={i}
textAnchor="middle"
x={x}
y={y}
>
{value.toLocaleString()}
</text>
)
})}
</g>
)
}
export function BarChartLabelDemo() {
return (
<div className="w-full p-4">
<BarChart data={data} xDataKey="product" margin={{ top: 50 }}>
<Grid horizontal />
<Bar dataKey="units" fill="hsl(280, 87%, 65%)" lineCap={4} />
<BarLabels dataKey="units" />
<BarXAxis />
<ChartTooltip />
</BarChart>
</div>
)
}
Legend
Desktop
Mobile
"use client"
import BarChart, {
Bar,
BarXAxis,
ChartTooltip,
Grid,
Legend,
LegendItem,
LegendLabel,
LegendMarker,
type LegendItemData,
} from "@/components/vritti/bar-chart"
const stackedData = [
{ month: "Jan", desktop: 4000, mobile: 2400 },
{ month: "Feb", desktop: 5000, mobile: 3000 },
{ month: "Mar", desktop: 3500, mobile: 2800 },
{ month: "Apr", desktop: 4200, mobile: 3200 },
{ month: "May", desktop: 3800, mobile: 2600 },
{ month: "Jun", desktop: 5500, mobile: 3800 },
]
const legendItems: LegendItemData[] = [
{ label: "Desktop", value: 0, color: "hsl(217, 91%, 60%)" },
{ label: "Mobile", value: 0, color: "hsl(217, 91%, 75%)" },
]
export function BarChartLegendDemo() {
return (
<div className="w-full p-4">
<BarChart data={stackedData} stacked stackGap={3} xDataKey="month">
<Grid horizontal />
<Bar
dataKey="desktop"
fill="hsl(217, 91%, 60%)"
lineCap={4}
stackGap={3}
/>
<Bar
dataKey="mobile"
fill="hsl(217, 91%, 75%)"
lineCap={4}
stackGap={3}
/>
<BarXAxis />
<ChartTooltip />
</BarChart>
<Legend
className="flex-row justify-center gap-6"
items={legendItems}
>
<LegendItem className="flex items-center gap-2">
<LegendMarker />
<LegendLabel />
</LegendItem>
</Legend>
</div>
)
}
Mixed
"use client"
import BarChart, { Bar, BarXAxis, Grid, ChartTooltip } from "@/components/vritti/bar-chart"
const data = [
{ month: "Jan", profit: 8200, loss: 3100 },
{ month: "Feb", profit: 5400, loss: 7800 },
{ month: "Mar", profit: 11200, loss: 2400 },
{ month: "Apr", profit: 3600, loss: 9500 },
{ month: "May", profit: 14800, loss: 1200 },
{ month: "Jun", profit: 7100, loss: 5600 },
{ month: "Jul", profit: 2300, loss: 11400 },
{ month: "Aug", profit: 16500, loss: 800 },
]
export function BarChartMixedDemo() {
return (
<div className="w-full p-4">
<BarChart data={data} xDataKey="month">
<Grid horizontal />
<Bar dataKey="profit" fill="hsl(142, 71%, 45%)" lineCap={4} />
<Bar dataKey="loss" fill="hsl(0, 91%, 65%)" lineCap={4} />
<BarXAxis />
<ChartTooltip />
</BarChart>
</div>
)
}
Narrow
"use client"
import BarChart, { Bar, BarXAxis, ChartTooltip, Grid } from "@/components/vritti/bar-chart"
const data = [
{ month: "Jan", revenue: 12000 },
{ month: "Feb", revenue: 15500 },
{ month: "Mar", revenue: 11000 },
{ month: "Apr", revenue: 18500 },
{ month: "May", revenue: 16800 },
{ month: "Jun", revenue: 21200 },
]
export function BarChartNarrowDemo() {
return (
<div className="w-full p-4">
<BarChart barGap={0.1} data={data} xDataKey="month">
<Grid horizontal />
<Bar
dataKey="revenue"
fill="var(--chart-line-primary)"
lineCap="round"
/>
<BarXAxis />
<ChartTooltip />
</BarChart>
</div>
)
}
No Gap
"use client"
import { useEffect, useState } from "react"
import { createPortal } from "react-dom"
import { motion, useSpring } from "motion/react"
import BarChart, {
Bar,
BarXAxis,
ChartTooltip,
Grid,
LinearGradient,
useChart,
} from "@/components/vritti/bar-chart"
const data = [
{ month: "Jan", revenue: 12000 },
{ month: "Feb", revenue: 15500 },
{ month: "Mar", revenue: 11000 },
{ month: "Apr", revenue: 18500 },
{ month: "May", revenue: 16800 },
{ month: "Jun", revenue: 21200 },
]
function AnimatedBarLine({
barX,
barTopY,
barBottomY,
width,
isHovered,
}: {
barX: number
barTopY: number
barBottomY: number
width: number
isHovered: boolean
}) {
const animatedY = useSpring(barBottomY, { stiffness: 300, damping: 30 })
const animatedOpacity = useSpring(0, { stiffness: 300, damping: 30 })
useEffect(() => {
animatedY.set(isHovered ? barTopY : barBottomY)
animatedOpacity.set(isHovered ? 1 : 0)
}, [isHovered, barTopY, barBottomY, animatedY, animatedOpacity])
return (
<motion.rect
fill="var(--chart-indicator-color)"
height={2}
style={{ opacity: animatedOpacity, y: animatedY }}
width={width}
x={barX}
/>
)
}
function BarHorizontalLineIndicator() {
const { barScale, bandWidth, innerHeight, margin, containerRef, hoveredBarIndex, yScale } =
useChart()
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
const container = containerRef.current
if (!(mounted && container && bandWidth && barScale)) {
return null
}
return createPortal(
<svg
aria-hidden="true"
className="pointer-events-none absolute inset-0 z-50"
height="100%"
width="100%"
>
<g transform={`translate(${margin.left},${margin.top})`}>
{data.map((d, i) => {
const barX = (barScale as (v: string) => number)(d.month) ?? 0
const barTopY = (yScale as (v: number) => number)(d.revenue) ?? innerHeight
const isHovered = hoveredBarIndex === i
return (
<AnimatedBarLine
barBottomY={innerHeight}
barTopY={barTopY}
barX={barX}
isHovered={isHovered}
key={d.month}
width={bandWidth}
/>
)
})}
</g>
</svg>,
container
)
}
export function BarChartNoGapDemo() {
return (
<div className="w-full p-4">
<BarChart barGap={0} data={data} xDataKey="month">
<LinearGradient
from="var(--chart-3)"
id="noGapGradient"
to="transparent"
/>
<Grid horizontal />
<Bar
dataKey="revenue"
fill="url(#noGapGradient)"
lineCap="butt"
stroke="var(--chart-3)"
/>
<BarXAxis />
<ChartTooltip showCrosshair={false} showDots={false} />
<BarHorizontalLineIndicator />
</BarChart>
</div>
)
}
Pattern
"use client"
import BarChart, { Bar, BarXAxis, ChartTooltip, Grid, PatternLines } from "@/components/vritti/bar-chart"
const data = [
{ month: "Jan", revenue: 12000 },
{ month: "Feb", revenue: 15500 },
{ month: "Mar", revenue: 11000 },
{ month: "Apr", revenue: 18500 },
{ month: "May", revenue: 16800 },
{ month: "Jun", revenue: 21200 },
]
export function BarChartPatternDemo() {
return (
<div className="w-full p-4">
<BarChart data={data} xDataKey="month">
<PatternLines
height={8}
id="barPattern"
orientation={["diagonal"]}
stroke="hsl(217, 91%, 60%)"
strokeWidth={2}
width={8}
/>
<Grid horizontal />
<Bar
dataKey="revenue"
fill="url(#barPattern)"
lineCap={4}
stroke="hsl(217, 91%, 60%)"
/>
<BarXAxis />
<ChartTooltip />
</BarChart>
</div>
)
}
Radial
"use client"
import BarChart, { Bar, BarYAxis, Grid, ChartTooltip } from "@/components/vritti/bar-chart"
const data = [
{ age: "18-24", male: 320, female: 290 },
{ age: "25-34", male: 480, female: 510 },
{ age: "35-44", male: 410, female: 390 },
{ age: "45-54", male: 350, female: 370 },
{ age: "55-64", male: 270, female: 250 },
{ age: "65+", male: 180, female: 210 },
]
export function BarChartDivergingDemo() {
return (
<div className="w-full p-4">
<BarChart
data={data}
xDataKey="age"
orientation="horizontal"
aspectRatio="4 / 3"
margin={{ left: 60 }}
>
<Grid fadeVertical horizontal={false} vertical />
<Bar dataKey="male" fill="hsl(217, 91%, 60%)" lineCap={4} />
<Bar dataKey="female" fill="hsl(330, 80%, 60%)" lineCap={4} />
<BarYAxis />
<ChartTooltip showCrosshair={false} />
</BarChart>
</div>
)
}
Stacked
"use client"
import BarChart, { Bar, BarXAxis, Grid, ChartTooltip } from "@/components/vritti/bar-chart"
const data = [
{ month: "Jan", desktop: 4000, mobile: 2400 },
{ month: "Feb", desktop: 5000, mobile: 3000 },
{ month: "Mar", desktop: 3500, mobile: 2800 },
{ month: "Apr", desktop: 4200, mobile: 3200 },
{ month: "May", desktop: 3800, mobile: 2600 },
{ month: "Jun", desktop: 5500, mobile: 3800 },
]
export function BarChartStackedDemo() {
return (
<div className="w-full p-4">
<BarChart data={data} xDataKey="month" stacked stackGap={3}>
<Grid horizontal />
<Bar dataKey="desktop" fill="hsl(217, 91%, 60%)" lineCap={4} stackGap={3} />
<Bar dataKey="mobile" fill="hsl(217, 91%, 75%)" lineCap={4} stackGap={3} />
<BarXAxis />
<ChartTooltip />
</BarChart>
</div>
)
}