charts
Registry
Bar Chart
Compound bar chart with stacked, horizontal, hatched, duotone, stripped, and gradient fills.
Preview
Preview
Revenue
$3.6k
Variants
Stacked
Hatched / duotone / stripped
Horizontal
Loading
Loading
Loading
Installation
Install with the shadcn CLI. This copies source into your project.
bash
npx shadcn@latest add https://livedocs.xyz/r/bar-chart.jsonNamespace (after one-time setup)
npx shadcn@latest add @livedocs/bar-chartUsage
tsx
import { monthlyData, trafficConfig } from "@/components/ui/chart"
import { BarChart } from "@/components/ui/bar-chart"
export function Example() {
return (
<BarChart title="Traffic" value="128,430" data={monthlyData} config={trafficConfig}>
<BarChart.Grid />
<BarChart.Tooltip />
<BarChart.Legend isClickable />
<BarChart.Bar dataKey="desktop" variant="default" isClickable />
<BarChart.Bar dataKey="mobile" isClickable />
</BarChart>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | — | Polar heading rendered above the chart |
| value | string | — | Headline metric rendered under the title |
| layout | "horizontal" | "vertical" | "horizontal" | Column vs row bars |
| stackType | "none" | "stacked" | "percent" | — | Stacking behavior |
| Bar.variant | "default" | "hatched" | "gradient" | "duotone" | "stripped" | "default" | Fill treatment |
Source
"use client";
import * as React from "react";
import {
Bar,
BarChart as RechartsBarChart,
Brush,
Cell,
ResponsiveContainer,
XAxis,
YAxis,
} from "recharts";
import {
ChartContainer,
ChartGrid,
ChartHeading,
ChartLegend,
ChartTooltip,
GradientFill,
HatchPattern,
colorVar,
useChart,
type ChartConfig,
} from "@/components/ui/chart";
import { type ChartReactionOptions } from "@/components/ui/chart-reactions";
import { cn } from "@/lib/utils";
type BarVariant =
| "default"
| "hatched"
| "gradient"
| "duotone"
| "stripped";
type BarSeriesProps = {
dataKey: string;
variant?: BarVariant;
isClickable?: boolean;
isGlowing?: boolean;
stackId?: string;
radius?: number;
};
function BarSeries(_props: BarSeriesProps) {
return null;
}
function Grid(props: React.ComponentProps<typeof ChartGrid>) {
return <ChartGrid {...props} />;
}
Grid.displayName = "CartesianGrid";
function AxisX(props: React.ComponentProps<typeof XAxis>) {
return <XAxis {...props} />;
}
AxisX.displayName = "XAxis";
function AxisY(props: React.ComponentProps<typeof YAxis>) {
return <YAxis {...props} />;
}
AxisY.displayName = "YAxis";
function Tooltip(props: React.ComponentProps<typeof ChartTooltip>) {
return <ChartTooltip {...props} />;
}
Tooltip.displayName = "Tooltip";
function Legend(props: React.ComponentProps<typeof ChartLegend>) {
return <ChartLegend {...props} />;
}
Legend.displayName = "Legend";
function ChartBrush(props: React.ComponentProps<typeof Brush>) {
return <Brush {...props} />;
}
ChartBrush.displayName = "Brush";
function ChartBar({
data,
config,
className,
children,
isLoading,
reaction,
defaultSelectedDataKey,
onSelectionChange,
xDataKey = "month",
layout = "horizontal",
stackType,
variant,
title,
value,
}: {
data: Record<string, unknown>[];
config: ChartConfig;
className?: string;
children: React.ReactNode;
isLoading?: boolean;
reaction?: ChartReactionOptions;
defaultSelectedDataKey?: string;
onSelectionChange?: (key?: string) => void;
xDataKey?: string;
layout?: "horizontal" | "vertical";
stackType?: "none" | "stacked" | "percent";
variant?: "panel" | "plain";
title?: string;
value?: string;
}) {
const childArray = React.Children.toArray(children);
const series = childArray.filter(
(child) => React.isValidElement(child) && child.type === BarSeries
) as React.ReactElement<BarSeriesProps>[];
const extras = childArray.filter(
(child) => !(React.isValidElement(child) && child.type === BarSeries)
);
return (
<ChartContainer
isLoading={isLoading}
loadingVariant="bar"
reaction={reaction}
config={config}
data={data}
className={cn("h-72 w-full", className)}
variant={variant}
defaultSelectedDataKey={defaultSelectedDataKey}
onSelectionChange={onSelectionChange}
>
<ChartHeading title={title} value={value} />
<div className="min-h-0 w-full flex-1">
<BarBody
data={data}
xDataKey={xDataKey}
series={series}
extras={extras}
layout={layout}
stackType={stackType}
/>
</div>
</ChartContainer>
);
}
function BarBody({
data,
xDataKey,
series,
extras,
layout,
stackType,
}: {
data: Record<string, unknown>[];
xDataKey: string;
series: React.ReactElement<BarSeriesProps>[];
extras: React.ReactNode[];
layout: "horizontal" | "vertical";
stackType?: "none" | "stacked" | "percent";
}) {
const { id, selected, setSelected } = useChart();
const [hovered, setHovered] = React.useState<{ key: string; index: number }>();
const stacked = stackType === "stacked" || stackType === "percent";
const vertical = layout === "vertical";
return (
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart
data={data}
layout={vertical ? "vertical" : "horizontal"}
stackOffset={stackType === "percent" ? "expand" : undefined}
barCategoryGap="30%"
margin={{ top: 8, right: 8, left: 0, bottom: 0 }}
>
<defs>
{series.map((item) => {
const key = item.props.dataKey;
const color = colorVar(key);
return (
<React.Fragment key={key}>
<GradientFill id={`${id}-${key}-fill`} color={color} startOpacity={0.85} endOpacity={0.4} />
<HatchPattern id={`${id}-${key}-hatch`} color={color} />
<linearGradient id={`${id}-${key}-duo`} x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stopColor={color} stopOpacity={1} />
<stop offset="100%" stopColor={color} stopOpacity={0.35} />
</linearGradient>
<pattern
id={`${id}-${key}-strip`}
width="8"
height="8"
patternUnits="userSpaceOnUse"
>
<rect width="8" height="8" fill={color} opacity="0.25" />
<rect width="4" height="8" fill={color} />
</pattern>
</React.Fragment>
);
})}
</defs>
{extras}
{series.map((item) => {
const {
dataKey,
variant = "default",
isClickable,
stackId,
radius = 4,
} = item.props;
const muted = selected && selected !== dataKey;
const fill =
variant === "hatched"
? `url(#${id}-${dataKey}-hatch)`
: variant === "stripped"
? `url(#${id}-${dataKey}-strip)`
: variant === "duotone"
? `url(#${id}-${dataKey}-duo)`
: variant === "gradient"
? `url(#${id}-${dataKey}-fill)`
: colorVar(dataKey);
return (
<Bar
key={dataKey}
dataKey={dataKey}
fill={fill}
radius={radius}
maxBarSize={28}
stackId={stacked ? stackId ?? "stack" : stackId}
activeBar={false}
onClick={() => isClickable && setSelected(dataKey)}
cursor={isClickable ? "pointer" : undefined}
>
{data.map((row, index) => (
<Cell
key={index}
opacity={hovered ? (hovered.key === dataKey && hovered.index === index ? 1 : 0.6) : muted ? 0.25 : 1}
onMouseEnter={() => setHovered({ key: dataKey, index })}
onMouseLeave={() => setHovered(undefined)}
onFocus={() => setHovered({ key: dataKey, index })}
onBlur={() => setHovered(undefined)}
tabIndex={0}
aria-label={`${row[xDataKey]}: ${dataKey} ${row[dataKey]}`}
className="transition-opacity duration-150 motion-reduce:transition-none"
/>
))}
</Bar>
);
})}
{extras.some(
(child) => React.isValidElement(child) && child.type === AxisX
) ? null : vertical ? (
<YAxis
dataKey={xDataKey}
type="category"
tickLine={false}
axisLine={false}
width={48}
tickFormatter={(value: string) =>
typeof value === "string" ? value.slice(0, 3) : value
}
/>
) : (
<XAxis
dataKey={xDataKey}
tickLine={false}
axisLine={false}
tickFormatter={(value: string) =>
typeof value === "string" ? value.slice(0, 3) : value
}
/>
)}
{vertical ? <XAxis type="number" hide /> : null}
</RechartsBarChart>
</ResponsiveContainer>
);
}
export const BarChart = Object.assign(ChartBar, {
Bar: BarSeries,
Grid,
XAxis: AxisX,
YAxis: AxisY,
Tooltip,
Legend,
Brush: ChartBrush,
});