chartcn

Bar Chart

Composable bar charts with grouped series, dual axes, annotations, and animated legend toggles.

Build bar charts from composable primitives. Combine BarMark with lines, areas, axes, cursors, tooltips, and legends in any configuration.

Basic bar chart

The simplest bar chart needs a Chart wrapper, one BarMark, and axes. Pass radius for rounded corners.

import { BarMark, Chart, Cursor, Grid, Tooltip, XAxis, YAxis } from "@exhibit/charts";

const data = [
  { month: "Jan", sales: 4200, target: 4000 },
  { month: "Feb", sales: 4800, target: 4500 },
  // ...
];

<Chart data={data} xDataKey="month" height={300}>
  <Grid horizontal />
  <XAxis />
  <YAxis tickFormat={(v) => `$${(v / 1000).toFixed(0)}k`} />
  <BarMark dataKey="sales" name="Sales" color="var(--color-chart-1)" radius={4} />
  <Cursor type="band" />
  <Tooltip />
</Chart>

Grouped bars

Add multiple BarMark components for grouped bars. Each bar series gets its own colour and is automatically positioned side-by-side within each category. Define your series as a config array and map over it for cleaner code.

const series = [
  { dataKey: "productA", name: "Product A", color: "var(--color-chart-1)" },
  { dataKey: "productB", name: "Product B", color: "var(--color-chart-2)" },
  { dataKey: "productC", name: "Product C", color: "var(--color-chart-3)" },
  { dataKey: "productD", name: "Product D", color: "var(--color-chart-4)" },
];

<Chart data={data} xDataKey="quarter" height={350}>
  <Grid horizontal />
  <XAxis />
  <YAxis />
  {series.map((s) => (
    <BarMark key={s.dataKey} {...s} radius={3} />
  ))}
  <Cursor highlight type="band" />
  <Tooltip />
  <Legend />
</Chart>

Bar styling

Control bar appearance with radius for rounded corners, and activeOpacity / inactiveOpacity for hover feedback.

<BarMark
  dataKey="sales"
  name="Sales"
  color="var(--color-chart-1)"
  radius={6}
  activeOpacity={1}
  inactiveOpacity={0.2}
/>
PropTypeDefaultDescription
radiusnumber0Corner radius in pixels
activeOpacitynumber1Opacity of bars in the hovered group (requires <Cursor highlight>)
inactiveOpacitynumber0.3Opacity of non-hovered bars

Cursor modes

The Cursor component supports both band and line types. Combine with highlight to dim non-hovered groups.

Band cursor with highlight

Line cursor with highlight

// Band cursor — shaded region over the active category
<Cursor type="band" highlight />

// Line cursor — thin vertical line
<Cursor type="line" highlight />
PropBehaviour
type="band"Shaded rectangle spanning the full category width. Best for bar charts with categorical X axes.
type="line"Dashed vertical line at the active data point. Works with both band and linear scales.
highlightWhen true, dims all non-hovered bar groups and non-closest lines to draw focus to the active data.

Mixed bar and line

Combine BarMark and LineMark in the same chart. Use dual Y axes when the bar and line series have different units or scales.

import {
  BarMark, Chart, Cursor, Grid, Legend, LineMark, Tooltip, XAxis, YAxis
} from "@exhibit/charts";

<Chart data={data} xDataKey="month" height={400} padding={{ left: 56, right: 56 }}>
  <Grid horizontal />
  <XAxis />
  <YAxis label="Precipitation" tickFormat={(v) => `${v} ml`} />
  <YAxis position="right" label="Temperature" tickFormat={(v) => `${v} °C`} />

  <BarMark dataKey="evaporation" name="Evaporation" color="var(--color-chart-1)" />
  <BarMark dataKey="precipitation" name="Precipitation" color="var(--color-chart-2)" />
  <BarMark dataKey="runoff" name="Runoff" color="var(--color-chart-4)" />
  <LineMark
    dataKey="temperature"
    name="Temperature"
    yAxisId="right"
    color="var(--color-chart-3)"
    markerPosition="snap"
  />

  <Cursor highlight type="band" />
  <Tooltip />
  <Legend />
</Chart>

Dual axes with bar + line overlay

Assign any mark to a second Y axis via yAxisId="right". This works for bars, lines, and areas.

<Chart data={data} xDataKey="month" height={380} padding={{ left: 56, right: 56 }}>
  <Grid horizontal />
  <XAxis />
  <YAxis label="kWh" />
  <YAxis position="right" label="Price" domain={[0, 0.4]} tickFormat={(v) => `$${v.toFixed(2)}`} />

  <BarMark dataKey="consumption" name="Consumption" color="var(--color-chart-1)" radius={2} />
  <BarMark dataKey="generation" name="Generation" color="var(--color-chart-2)" radius={2} />
  <LineMark
    dataKey="price"
    name="Price/kWh"
    yAxisId="right"
    color="#f59e0b"
    curve="monotoneX"
    dot
    markerPosition="snap"
    strokeWidth={2.5}
  />

  <Cursor highlight type="band" />
  <Tooltip>{/* custom tooltip */}</Tooltip>
  <Legend />
</Chart>

Area + bar combination

Layer AreaMark and BarMark in the same chart. The area renders behind the bars, creating a contextual backdrop for the bar data.

import { AreaMark, BarMark, Chart, Cursor, Grid, Legend, Tooltip, XAxis, YAxis } from "@exhibit/charts";

<Chart data={data} xDataKey="hour" height={350} padding={{ left: 56, right: 56 }}>
  <Grid horizontal />
  <XAxis />
  <YAxis label="Vehicles" />
  <YAxis position="right" label="Incidents" domain={[0, 12]} />

  <AreaMark dataKey="volume" name="Traffic volume" color="var(--color-chart-1)" fillOpacity={0.12} />
  <BarMark dataKey="incidents" name="Incidents" yAxisId="right" color="#ef4444" radius={3} />

  <Cursor highlight type="band" />
  <Tooltip>{/* custom tooltip */}</Tooltip>
  <Legend />
</Chart>

Custom tooltips

Pass a render function to <Tooltip> for full control. Combine TooltipShell, TooltipHeader, and TooltipRow with your own markup for computed values, badges, or conditional formatting.

<Tooltip>
  {({ datum }) => {
    const sales = typeof datum.sales === "number" ? datum.sales : 0;
    const target = typeof datum.target === "number" ? datum.target : 0;
    const delta = sales - target;
    const pct = target > 0 ? ((delta / target) * 100).toFixed(1) : "0";

    return (
      <TooltipShell className="min-w-44">
        <TooltipHeader>{String(datum.month)}</TooltipHeader>
        <TooltipRow color="var(--color-chart-1)" indicator="square" label="Sales" value={`$${sales.toLocaleString()}`} />
        <TooltipRow color="var(--color-chart-4)" indicator="dot" label="Target" value={`$${target.toLocaleString()}`} />
        <div className="border-border mt-1 border-t pt-1">
          <span className={`text-xs font-medium ${delta >= 0 ? "text-green-600" : "text-red-600"}`}>
            {delta >= 0 ? "+" : ""}{pct}% vs target
          </span>
        </div>
      </TooltipShell>
    );
  }}
</Tooltip>

Annotations and reference lines

Use ReferenceLine for horizontal or vertical reference markers and Annotation for point markers with labels. These compose naturally with bar charts to highlight averages, thresholds, and extremes.

import { Annotation, BarMark, Chart, ReferenceLine, ... } from "@exhibit/charts";

<Chart data={data} xDataKey="month" height={420} padding={{ left: 48, right: 48, top: 24, bottom: 40 }}>
  <Grid horizontal />
  <XAxis />
  <YAxis />

  <BarMark dataKey="rainfall" name="Rainfall" color="#5470c6" radius={2} />
  <BarMark dataKey="evaporation" name="Evaporation" color="#91cc75" radius={2} />

  {/* Average reference lines */}
  <ReferenceLine y={41.5} color="#5470c6" label="Avg: 41.5" strokeDasharray="6 4" />
  <ReferenceLine y={48.0} color="#91cc75" label="Avg: 48.0" labelPosition="start" strokeDasharray="6 4" />

  {/* Max/min annotations aligned to specific bar series */}
  <Annotation x="Aug" y={162.2} barAlign="rainfall" color="#5470c6" label="Max: 162.2" />
  <Annotation x="Jan" y={2.0} barAlign="rainfall" color="#5470c6" label="Min: 2.0" labelPosition="bottom" />

  <Cursor type="band" />
  <Tooltip />
  <Legend />
</Chart>

Props

Chart

PropTypeDefaultDescription
dataT[]requiredArray of data objects
xDataKeystring & keyof TrequiredKey used for the X axis
heightnumber400Chart height in pixels
paddingPartial<Padding>{ top: 20, right: 20, bottom: 40, left: 48 }Padding around the plot area
animatebooleantrueEnable entrance animations
classNamestring--Additional CSS class

BarMark

PropTypeDefaultDescription
dataKeystringrequiredData key for this bar series
namestring--Display name (used in legend and tooltip)
colorstringvar(--color-chart-1)Bar fill colour
yAxisId"left" | "right""left"Which Y axis to bind to
radiusnumber0Bar corner radius
activeOpacitynumber1Opacity of hovered bars (when highlight is on)
inactiveOpacitynumber0.3Opacity of non-hovered bars

ReferenceLine

PropTypeDefaultDescription
ynumber--Horizontal line at this Y value
xnumber | string--Vertical line at this X value
colorstringcurrentColorLine and label colour
labelstring--Text label
labelPosition"start" | "end""end"Where to place the label
strokeDasharraystring"6 4"Dash pattern (set to "" for solid)
strokeOpacitynumber0.6Line opacity
strokeWidthnumber1Line thickness
yAxisId"left" | "right""left"Which Y axis to reference

Annotation

PropTypeDefaultDescription
xnumber | stringrequiredX position (category name or numeric value)
ynumberrequiredY value
barAlignstring--Align marker to a specific bar in a grouped chart
colorstringcurrentColorMarker and label colour
labelstring--Label text
labelPosition"top" | "bottom""top"Show label above or below the marker
sizenumber5Marker radius
yAxisId"left" | "right""left"Which Y axis to reference

Cursor

PropTypeDefaultDescription
axis"x" | "y""x"Which axis the cursor aligns to
labelboolean | (value) => ReactNode--Display the active axis value
markerboolean | { className?: string; behaviour?: "interpolate" | "snap" | "absolute" }--Render a dot at the cursor/series intersection
type"line" | "band""line"Cursor style
highlightbooleanfalseDim non-hovered bars and non-closest lines
lineColorstring--Line cursor colour
lineWidthnumber1.5Line cursor width
lineDashstring"4 4"Line cursor dash pattern
bandColorstring--Band cursor fill colour
bandOpacitynumber0.06Band cursor fill opacity

Tooltip

PropTypeDefaultDescription
childrenReactNode | (props: TooltipRenderProps) => ReactNode--Custom content (auto-generated if omitted)
offset{ x?: number; y?: number }{ x: 16, y: -8 }Offset from cursor
stiffnessnumber0.18Spring stiffness for follow animation (0-1)

Legend

PropTypeDefaultDescription
itemsLegendItem[]--Explicit items (auto-generated from marks if omitted)
position"top" | "bottom""bottom"Position relative to the chart
classNamestring--Additional CSS class

XAxis / YAxis

PropTypeDefaultDescription
labelstring--Axis label
tickFormat(value) => string--Custom tick label formatter
tickCountnumberautoNumber of ticks
position"left" | "right""left"Y axis position
domain[number | "auto", number | "auto"]autoY axis domain
hideAxisLinebooleanfalseHide the axis line

Grid

PropTypeDefaultDescription
horizontalbooleantrueShow horizontal grid lines
verticalbooleanfalseShow vertical grid lines
colorstring--Grid line colour
strokeDasharraystring"3 3"Dash pattern

On this page