2

Bar Chart

PreviousNext

A flexible bar chart component for visualizing categorical data with support for multiple variants including horizontal, stacked, grouped, and interactive layouts.

"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}
      color="#2563eb"
      showGrid
      showTooltip
      barRadius={0}
      aspectRatio={2.5}
    />
  )
}

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

Installation

pnpm dlx shadcn@latest add https://ui.simplifying.ai/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}
      color="#2563eb"
      showGrid
      showTooltip
      barRadius={0}
      aspectRatio={2.5}
    />
  )
}

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.

Desktop
Mobile
"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.

Desktop
Mobile
"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.

Desktop
Trend
"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

PropTypeDefaultDescription
dataBarChartDataPoint[]requiredArray of data points to display
colorstring"#2563eb"Fill color for bars
classNamestring-Additional CSS classes
showGridbooleantrueShow horizontal grid lines
showTooltipbooleantrueEnable tooltip on hover
showLabelbooleanfalseShow 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)
barRadiusnumber4Corner radius of bars
aspectRationumber2Width to height ratio
yAxisWidthnumber48Width reserved for Y-axis labels

MultiBarChart

PropTypeDefaultDescription
dataMultiBarChartDataPoint[]requiredArray of data points
seriesMultiBarChartSeries[]requiredConfiguration for each data series
classNamestring-Additional CSS classes
showGridbooleantrueShow horizontal grid lines
showTooltipbooleantrueEnable tooltip on hover
showLegendbooleantrueShow legend below chart
valueFormatter(value: number) => string-Format displayed values
labelFormatter(label: string) => string-Format category labels
layout"vertical" | "horizontal""horizontal"Bar orientation
barRadiusnumber4Corner radius of bars
aspectRationumber2Width to height ratio
yAxisWidthnumber48Width 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 MultiBarChart component 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)