Bar Chart

Renders a bar chart for data visualization.

Installation

pnpm dlx shadcn@latest add https://ui.vllnt.com/r/bar-chart.json
bash

Code

import * as React from "react";

import { cn } from "../../lib/utils";

type Datum = {
  label?: string;
  value: number;
};

export type BarChartProps = {
  color?: string;
  data: Datum[];
  gap?: number;
  gradientId?: string;
  height?: number;
  width?: number;
} & React.HTMLAttributes<HTMLDivElement>;

const DEFAULT_WIDTH = 320;
const DEFAULT_HEIGHT = 140;
const DEFAULT_GAP = 12;

export const BarChart = React.forwardRef<HTMLDivElement, BarChartProps>(
  (
    {
      className,
      color = "currentColor",
      data,
      gap = DEFAULT_GAP,
      gradientId = "bar-chart-gradient",
      height = DEFAULT_HEIGHT,
      width = DEFAULT_WIDTH,
      ...props
    },
    reference,
  ) => {
    if (data.length === 0) {
      return null;
    }

    const values = data.map((point) => point.value);
    const maxValue = Math.max(...values, 0);
    const normalizedMax = maxValue || 1;
    const totalGap = gap * Math.max(data.length - 1, 0);
    const barWidth = Math.max((width - totalGap) / data.length, 6);

    return (
      <div
        className={cn(
          "rounded-lg border border-border bg-background/40 p-3",
          className,
        )}
        ref={reference}
        {...props}
      >
        <svg
          aria-label="Bar chart"
          className="h-full w-full"
          height={height}
          role="img"
          viewBox={`0 0 ${width} ${height}`}
          width={width}
        >
          <defs>
            <linearGradient id={gradientId} x1="0" x2="0" y1="0" y2="1">
              <stop offset="0%" stopColor={color} stopOpacity="0.9" />
              <stop offset="100%" stopColor={color} stopOpacity="0.4" />
            </linearGradient>
          </defs>
          {data.map((point, index) => {
            const barHeight = (point.value / normalizedMax) * (height - 12);
            const x = index * (barWidth + gap);
            const y = height - barHeight;
            return (
              <rect
                fill={`url(#${gradientId})`}
                height={barHeight}
                key={`${point.label ?? index}-${point.value}`}
                rx={4}
                width={barWidth}
                x={x}
                y={y}
              >
                <title>
                  {point.label ? `${point.label}: ` : ""}
                  {point.value.toLocaleString()}
                </title>
              </rect>
            );
          })}
        </svg>
      </div>
    );
  },
);

BarChart.displayName = "BarChart";
typescript