Bar Chart
BarChart is the bar primitive used inside Chart — use it directly when you need per-series color overrides, click handlers, or grouped-stacked layout. It supports the following:
- Each series is defined by a
BarSeriesConfig({ key, label?, color, unit?, stackId? }) so series order, color, and tooltip labels are explicit. layoutcontrols how multiple series are arranged:single,grouped,stacked, orgrouped-stacked(mix of stacking and grouping viastackId).orientation="horizontal"flips the axes — use it for long category labels.- All click callbacks (
onBarClick,onXAxisTickClick,onYAxisTickClick) receive the native mouse event as the last argument so you can calle.stopPropagation(). uniton a series adds a suffix to its tooltip value.
Import
import { BarChart } from "h2o-library/charts";Usage
Single
One metric across categories.
Cases by specialty
const data = [
{ x: "Bariatrics", cases: 35 },
{ x: "Cardiology", cases: 78 },
{ x: "Dermatology", cases: 23 },
];
<BarChart
data={data}
bars={[{ key: "cases", label: "Cases", color: "#041957" }]}
layout="single"
/>;Grouped
Side-by-side bars per category for multi-metric comparison.
Appointments by month
<BarChart
data={data}
bars={[
{ key: "completed", label: "Completed", color: "#041957" },
{ key: "missed", label: "Missed", color: "#3767F6" },
]}
layout="grouped"
/>;Stacked
Stack series into one bar per category for part-to-whole comparison.
Cases by specialty
<BarChart
data={data}
bars={[
{ key: "cardiology", label: "Cardiology", color: "#041957" },
{ key: "dermatology", label: "Dermatology", color: "#3767F6" },
{ key: "neurology", label: "Neurology", color: "#9FB7FF" },
]}
layout="stacked"
/>;Grouped-Stacked
Stack some bars while keeping other groups side-by-side. Assign stackId to each bar — bars with the same stackId stack together; distinct stackId values are placed side-by-side.
Revenue by product line vs services
const bars = [
{
key: "productA",
label: "Product A",
color: "#041957",
stackId: "products",
},
{
key: "productB",
label: "Product B",
color: "#3767F6",
stackId: "products",
},
{
key: "serviceA",
label: "Service A",
color: "#e94040",
stackId: "services",
},
{
key: "serviceB",
label: "Service B",
color: "#f08080",
stackId: "services",
},
];
<BarChart data={data} bars={bars} layout="grouped-stacked" />;Per-Bar Labels
In a grouped chart every bar in a group shares one x-axis category, yet the bars are often different entities that change from group to group — so a single legend can't name them. Point labelKey at a field on each data point to draw that bar's own name above the bar instead.
The label is clamped to the bar width — wrapping onto new lines while it fits, then truncating with an ellipsis (hover shows the full text, e.g. "Priya Natarajan" below). Override the text color per series with labelColor. Omit labelKey to draw no label, so existing charts are unaffected.
For a stacked bar, a bar is the whole stack: put labelKey on the top-of-stack series only, so the name is drawn once above the stack rather than once per layer.
Top rep per region (score)
const data = [
{
x: "Region North",
first: 92,
second: 78,
third: 61,
firstName: "Alice",
secondName: "Bob",
thirdName: "Priya Natarajan",
},
// …names differ per group
];
const bars = [
{ key: "first", color: "#041957", labelKey: "firstName" }, // ← name drawn above the bar
{ key: "second", color: "#3767F6", labelKey: "secondName" },
{ key: "third", color: "#6f9bff", labelKey: "thirdName" },
];
<BarChart data={data} bars={bars} layout="grouped" />;For a grouped-stacked chart, a bar is a stack — set labelKey on the top-of-stack series only so the name is drawn once above the stack:
Revenue by stream (grouped-stacked)
const bars = [
{ key: "productA", label: "License", color: "#041957", stackId: "products" },
{
key: "productB",
label: "Add-ons",
color: "#3767F6",
stackId: "products",
labelKey: "productName", // ← top of the "products" stack
},
{
key: "serviceA",
label: "Onboarding",
color: "#e94040",
stackId: "services",
},
{
key: "serviceB",
label: "Support",
color: "#f08080",
stackId: "services",
labelKey: "serviceName", // ← top of the "services" stack
},
];
<BarChart data={data} bars={bars} layout="grouped-stacked" />;Horizontal
orientation="horizontal" flips the axes — use it when category labels are long and would otherwise overlap on the X axis. Long labels truncate with ellipsis and surface their full text in the native tooltip on hover.
Quarterly KPI scorecard
<BarChart
data={data}
bars={[{ key: "score", label: "Score", color: "#041957", unit: "%" }]}
layout="single"
orientation="horizontal"
/>;Axis Tick Formatters
xAxisTickFormatter and yAxisTickFormatter transform each tick's raw value into display text — append a unit, abbreviate large numbers (120000 → $120k), or map a code to a label — without touching the data. Formatting is display-only; onXAxisTickClick / onYAxisTickClick still receive the original raw value.
Monthly revenue
<BarChart
data={data}
bars={bars}
layout="single"
yAxisTickFormatter={(value) => {
const n = Number(value);
return n >= 1000 ? `$${(n / 1000).toFixed(0)}k` : `$${n}`;
}}
/>;Tooltip Formatter
tooltipFormatter controls the value line of the hover tooltip. It receives the raw numeric value and a context object (dataKey, name, unit, label, dataPoint) and returns the full value line — the category label above it is kept. When omitted, the default "<name>: <value> <unit>" line is rendered, so existing charts are unaffected.
Monthly revenue
<BarChart
data={data}
bars={bars}
layout="single"
tooltipFormatter={(value, { name }) =>
`${name}: ${new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0,
}).format(value)}`
}
/>;Custom Y-Axis Domain
By default the value axis starts at 0 and ends at a data-derived "nice" max. yAxisDomain={[min, max]} overrides either end; pass "auto" for an end to keep its default. Raising the floor emphasizes differences between bars that cluster in a narrow high range. In orientation="horizontal" this controls the value (X) axis.
NPS score (floor at 50)
// Fixed floor + fixed ceiling
<BarChart data={data} bars={bars} layout="single" yAxisDomain={[50, 100]} />
// Keep auto max, only raise the floor
<BarChart data={data} bars={bars} layout="single" yAxisDomain={[50, "auto"]} />
Click Events
onBarClick, onXAxisTickClick, and onYAxisTickClick all expose the native mouse event so you can stop propagation when needed.
<BarChart
data={data}
bars={bars}
onBarClick={(dataPoint, index, dataKey, e) => {
e.stopPropagation();
console.log(dataKey, dataPoint);
}}
onXAxisTickClick={(value, e) => {
e.stopPropagation();
console.log("tick:", value);
}}
/>;Units in Tooltips
Setting unit on a series appends it to the value in the hover tooltip — useful for %, kg, min, etc.
<BarChart
data={data}
bars={[
{ key: "utilization", label: "Utilization", color: "#041957", unit: "%" },
]}
/>;Playground
Keyboard
Interactive regions are only added when the matching click callback is passed (onBarClick, onXAxisTickClick, onYAxisTickClick). Those targets use role="button", tabIndex={0}, and activate with Enter or Space (same as click). Tab moves between focusable tick labels and bar segments that have handlers.
Props
BarChart
| Prop | Type | Default | Description |
|---|---|---|---|
data* | BarChartDataPoint[] | — | Array of data objects. Each must have an x field for the category axis and numeric fields matching bar keys. |
bars* | BarSeriesConfig[] | — | Series definitions — each specifies the data key, color, and optional label, unit, and stackId. |
layout | "single" | "grouped" | "stacked" | "grouped-stacked" | "single" | How multiple series are arranged. |
orientation | "vertical" | "horizontal" | "vertical" | Vertical draws bars upward; horizontal draws bars rightward (X/Y axes swapped). |
height | number | 400 | Height of the chart in pixels. |
showGrid | boolean | true | Renders a background grid behind the bars. |
showLegend | boolean | false | Shows the built-in Recharts legend. |
className | string | undefined | Additional CSS class for the container div. |
onBarClick | (dataPoint, index, dataKey, event) => void | undefined | Fired when a bar segment is clicked. Receives the data point, its index, the series key, and the native mouse event. |
onXAxisTickClick | (value, event) => void | undefined | Fired when an X-axis tick label is clicked. |
onYAxisTickClick | (value, event) => void | undefined | Fired when a Y-axis tick label is clicked. |
xAxisTickFormatter | (value) => string | undefined | Formats each X-axis tick label for display. Does not change the underlying data or the value passed to onXAxisTickClick. |
yAxisTickFormatter | (value) => string | undefined | Formats each Y-axis tick label for display. Does not change the underlying data or the value passed to onYAxisTickClick. |
yAxisDomain | [number | "auto", number | "auto"] | ["auto", "auto"] | Value-axis domain as [min, max] (Y when vertical, X when horizontal). Each end accepts "auto" to keep its default (0 for min, a data-derived nice value for max). |
tooltipFormatter | (value, ctx) => ReactNode | undefined | Formats the tooltip value line. ctx = { dataKey, name, unit?, label, dataPoint }. Returns the full value line; the category label above it is kept. |
* Required
BarSeriesConfig
| Prop | Type | Default | Description |
|---|---|---|---|
key* | string | — | Key in the data object this series reads from. |
color* | string | — | Fill color (hex, hsl, or any CSS color). |
label | string | key | Human-readable name shown in legend and tooltip. |
unit | string | undefined | Unit suffix appended in the tooltip (e.g. "%", "kg"). |
stackId | string | undefined | Required when layout="grouped-stacked". Bars with the same stackId stack; different stackIds are grouped side-by-side. |
labelKey | string | undefined | Key on each data point whose value is drawn as a name above this bar. Clamped to the bar width — wraps onto new lines then truncates with an ellipsis. For a stacked bar, set it on the top-of-stack series so the name shows once. Omit to draw no label. |
labelColor | string | hsl(var(--primary-foreground)) | Color of the per-bar label text. |
* Required
BarChartDataPoint
| Prop | Type | Default | Description |
|---|---|---|---|
x* | string | — | Category label shown on the category axis. |
[key] | number | string | — | Numeric values for each bar series, keyed by the corresponding BarSeriesConfig.key. |
* Required
Design Guidelines
- Use
singlefor one metric across categories. - Use
groupedfor side-by-side multi-metric comparison. - Use
stackedfor part-to-whole comparison; keep series count ≤ 5. - Use
grouped-stackedwhen you need both stacking within a group and grouping between groups. - Use
orientation="horizontal"when category names are long — it avoids tick-label overlap. - For headline pages with one or two charts, prefer the
Chartwrapper — it adds the title, KPI strip, and legend out of the box.
Accessibility
- Tooltips appear on mouse hover and are not keyboard-accessible by default.
- For critical data, provide a companion data table.
onBarClick,onXAxisTickClick, andonYAxisTickClickmake individual bars and axis ticks interactive — pair them with page-level keyboard handlers if you need keyboard parity.