Skip to main content
Gremorie

Bar Chart

Grouped bars across categorical buckets, color-tokened by series with shared Y domain.

Overview

BarChart is a recharts chart wired to Gremorie's design tokens through the shadcn chart primitive (ChartContainer) for categorical comparison. Pass tabular data, a serializable config mapping each value field to a label plus a color, and the xKey for the category field. ChartContainer injects each series color as var(--color-<key>), and recharts renders the cartesian grid, axis ticks, and one set of grouped rectangles per series. Pass stacked, horizontal, or showLabels to switch layouts.

Reach for BarChart when discrete buckets matter - revenue by product, errors by service, users by region. For ordered-domain trends prefer LineChart or AreaChart; for two numeric axes prefer ScatterChart.

The React edition (@gremorie/rx-data) renders with recharts inside the shadcn chart primitive; the Angular edition (@gremorie/ng-data) renders headless d3 geometry inside a styled SVG. The rendering engine differs; the public surface is identical input-for-input.

Anatomy

<bar-chart>
└─ figure (role="img")                  card surface, aspect-video
   ├─ svg (chart frame)
   │  ├─ cartesian grid
   │  ├─ x axis (category from xKey)
   │  ├─ y axis (numeric, optional via yAxis)
   │  ├─ bars, one group per config key   (stacked or grouped)
   │  └─ value labels (optional via showLabels)
   ├─ tooltip overlay (optional via tooltip)
   ├─ legend (when config has >1 series)
   └─ sr-only data table

The React edition composes this surface through the shadcn chart primitive:

BarChart                       recharts bar chart wired to the chart primitive
├─ ChartContainer              responsive frame + injects --color-<key> from config
│  └─ RechartsBarChart         the recharts <BarChart> (accessibilityLayer)
│     ├─ CartesianGrid         horizontal rules (vertical when horizontal)
│     ├─ XAxis / YAxis         category + value axes (axes swap when horizontal)
│     ├─ ChartTooltip          hover card
│     │  └─ ChartTooltipContent  token-styled tooltip body
│     └─ Bar                   one per series; rounded corners
│        ├─ Cell               per-bar fill on a single categorical series
│        └─ LabelList          optional value drawn on each bar

Preview

Bar chart of Desktop, Mobile by month
monthDesktopMobile
Jan18680
Feb305200
Mar237120
Apr173190
May209130
Jun214140
'use client';import { BarChart, type ChartConfig, type ChartDatum } from '@gremorie/rx-data';const monthlyData: ChartDatum[] = [  { month: 'Jan', desktop: 186, mobile: 80 },  { month: 'Feb', desktop: 305, mobile: 200 },  { month: 'Mar', desktop: 237, mobile: 120 },  { month: 'Apr', desktop: 173, mobile: 190 },  { month: 'May', desktop: 209, mobile: 130 },  { month: 'Jun', desktop: 214, mobile: 140 },];const monthlyConfig: ChartConfig = {  desktop: { label: 'Desktop', color: 'var(--chart-1)' },  mobile: { label: 'Mobile', color: 'var(--chart-2)' },};export function BarChartPreview() {  return <BarChart data={monthlyData} config={monthlyConfig} xKey="month" />;}

Installation

bash npx gremorie@latest add rx-bar-chart
bash pnpm dlx gremorie@latest add rx-bar-chart
bash yarn dlx gremorie@latest add rx-bar-chart
bash bunx --bun gremorie@latest add rx-bar-chart

Usage

import { BarChart } from "@gremorie/rx-data";
import type { ChartConfig, ChartDatum } from "@gremorie/rx-data";

const data: ChartDatum[] = [
{ month: "Jan", desktop: 186, mobile: 80 },
{ month: "Feb", desktop: 305, mobile: 200 },
{ month: "Mar", desktop: 237, mobile: 120 },
{ month: "Apr", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "Jun", desktop: 214, mobile: 140 },
];

const config: ChartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" },
};

export function VisitsByMonth() {
  return <BarChart data={data} config={config} xKey="month" />;
}
import { Component } from '@angular/core';
import { BarChart } from '@gremorie/ng-data';
import type { ChartConfig, ChartDatum } from '@gremorie/ng-data';

@Component({
selector: 'app-visits-by-month',
imports: [BarChart],
template: `<bar-chart [data]="data" [config]="config" xKey="month" />`,
})
export class VisitsByMonthComponent {
readonly data: ChartDatum[] = [
{ month: 'Jan', desktop: 186, mobile: 80 },
{ month: 'Feb', desktop: 305, mobile: 200 },
{ month: 'Mar', desktop: 237, mobile: 120 },
{ month: 'Apr', desktop: 73, mobile: 190 },
{ month: 'May', desktop: 209, mobile: 130 },
{ month: 'Jun', desktop: 214, mobile: 140 },
];

readonly config: ChartConfig = {
desktop: { label: 'Desktop', color: 'var(--chart-1)' },
mobile: { label: 'Mobile', color: 'var(--chart-2)' },
};
}

API

<BarChart>

The styled wrapper. Renders a ChartContainer div wrapping a recharts <BarChart> (with accessibilityLayer enabled): a cartesian grid, X and Y axes, and one <Bar> per series in config, each filled with var(--color-<key>).

PropTypeDefaultDescription
datareadonly ChartDatum[]-Tabular data. Each row is a record from string keys to numeric or category values. The column at xKey is the category; every key in config must exist on every row as a numeric value. For per-bar colors on a single series, give each row a fill (e.g. var(--chart-1)).
configChartConfig-Maps each series key to { label, color }. The color is any CSS color or token, typically "var(--chart-1)" through "var(--chart-5)"; ChartContainer exposes it as var(--color-<key>).
xKeystring-Name of the category field on each row. Drives the category axis ticks (X by default, Y when horizontal).
stackedbooleanfalseStack the series within each category instead of grouping them side by side.
horizontalbooleanfalseRender horizontal bars (recharts layout="vertical").
showLabelsbooleanfalseDraw the value as a label on top of (or beside) each bar.
tooltipbooleantrueShow the hover tooltip.
radiusnumber8Corner radius of the bars.
classNamestring-Merged onto the ChartContainer. The bundled style supplies a rounded card surface plus aspect ratio.

Chart primitive exports

@gremorie/rx-data re-exports the shadcn chart primitive so you can compose custom charts. Wrap raw recharts (Bar, Area, Line, CartesianGrid, XAxis, YAxis) inside ChartContainer:

ExportRole
ChartContainerProvides the chart context, the responsive frame, and injects --color-<key> CSS vars from config.
ChartTooltip / ChartTooltipContentToken-styled hover tooltip.
ChartLegend / ChartLegendContentToken-styled legend.
useChartHook returning the active ChartConfig from context.

Types

TypeShape
ChartDatumRecord<string, string | number> & { fill?: string } - one row of chart data; the optional fill sets a per-bar color.
ChartConfigRecord<string, { label?: ReactNode; color?: string }> - per-series label and color map.

Composition

  1. Pick xKey - the categorical column that becomes the bottom axis.
  2. Author config mapping each value field to a label and a token-driven color. recharts auto-sizes the Y domain to cover every series.
  3. Pass data as an array of records.
  4. For custom rendering (extra reference lines, a custom legend, mixed series), compose raw recharts inside <ChartContainer> (see Composing with the chart primitive).

The chart auto-sizes to its parent via recharts' ResponsiveContainer, so the wrapping surface controls the width. The default class on the ChartContainer pins the chart to aspect-video.

Variations

Single series

Bar chart of Desktop by month
monthDesktop
Jan186
Feb305
Mar237
Apr173
May209
Jun214
'use client';import { BarChart, type ChartConfig, type ChartDatum } from '@gremorie/rx-data';const monthlyData: ChartDatum[] = [  { month: 'Jan', desktop: 186, mobile: 80 },  { month: 'Feb', desktop: 305, mobile: 200 },  { month: 'Mar', desktop: 237, mobile: 120 },  { month: 'Apr', desktop: 173, mobile: 190 },  { month: 'May', desktop: 209, mobile: 130 },  { month: 'Jun', desktop: 214, mobile: 140 },];const singleConfig: ChartConfig = {  desktop: { label: 'Desktop', color: 'var(--chart-1)' },};export function BarChartSinglePreview() {  return <BarChart data={monthlyData} config={singleConfig} xKey="month" />;}
<BarChart
  data={data}
  config={{ revenue: { label: 'Revenue', color: 'var(--chart-1)' } }}
  xKey="region"
/>
<bar-chart [data]="data" [config]="config" xKey="region" />

The default shape. Reach for a single bar series when comparing one metric across buckets.

Multi-series side by side

Bar chart of Desktop, Mobile by month
monthDesktopMobile
Jan18680
Feb305200
Mar237120
Apr173190
May209130
Jun214140
const config: ChartConfig = {
  desktop: { label: 'Desktop', color: 'var(--chart-1)' },
  mobile: { label: 'Mobile', color: 'var(--chart-2)' },
};

<BarChart data={data} config={config} xKey="month" />;
<bar-chart [data]="data" [config]="config" xKey="month" />

For comparing two or three series within the same bucket. Bars from different series share the X tick by default; keep the series count short (two or three) before the bars get too thin to read.

Stacked

Set stacked to stack the series within each category instead of grouping them side by side. Use it when the cumulative total per bucket carries meaning.

<BarChart data={data} config={config} xKey="month" stacked />
<bar-chart [data]="data" [config]="config" xKey="month" [stacked]="true" />

Horizontal

Set horizontal to swap the axes so the category runs down the Y axis. Reach for it when category labels are long or the bucket count is high.

<BarChart data={data} config={config} xKey="month" horizontal />
<bar-chart [data]="data" [config]="config" xKey="month" [horizontal]="true" />

Value labels

Set showLabels to draw the value as a label on top of (or beside) each bar, so exact magnitudes read without hovering.

<BarChart data={data} config={config} xKey="month" showLabels />
<bar-chart [data]="data" [config]="config" xKey="month" [showLabels]="true" />

Pair showLabels with radius (default 8) to soften the bar corners; lower it to 0 for square caps when labels sit flush on top.

Per-bar colors

For a single-series chart, give each row its own fill to color bars categorically. The per-row fill overrides the series color.

const data: ChartDatum[] = [
  { browser: 'Chrome', visitors: 275, fill: 'var(--chart-1)' },
  { browser: 'Safari', visitors: 200, fill: 'var(--chart-2)' },
  { browser: 'Firefox', visitors: 187, fill: 'var(--chart-3)' },
];

<BarChart
  data={data}
  config={{ visitors: { label: 'Visitors' } }}
  xKey="browser"
/>;
readonly data: ChartDatum[] = [
  { browser: 'Chrome', visitors: 275, fill: 'var(--chart-1)' },
  { browser: 'Safari', visitors: 200, fill: 'var(--chart-2)' },
  { browser: 'Firefox', visitors: 187, fill: 'var(--chart-3)' },
];
readonly config: ChartConfig = { visitors: { label: 'Visitors' } };
// <bar-chart [data]="data" [config]="config" xKey="browser" />

Composing with the chart primitive

When you need full control, skip BarChart and compose raw recharts inside ChartContainer. The container still injects your token colors and renders the tooltip styling, so you only write the recharts you actually need.

import {
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
} from '@gremorie/rx-data';
import { Bar, BarChart, CartesianGrid, XAxis } from 'recharts';

<ChartContainer config={config}>
  <BarChart accessibilityLayer data={data}>
    <CartesianGrid vertical={false} />
    <XAxis dataKey="month" tickLine={false} axisLine={false} tickMargin={10} />
    <ChartTooltip cursor={false} content={<ChartTooltipContent />} />
    <Bar dataKey="desktop" fill="var(--color-desktop)" radius={8} />
    <Bar dataKey="mobile" fill="var(--color-mobile)" radius={8} />
  </BarChart>
</ChartContainer>;

Compose raw recharts when the surface needs extra reference lines, a custom legend, or mixed series. ChartContainer owns the responsive frame and the token color vars.

Accessibility

  • Accessibility layer: recharts' accessibilityLayer is enabled, providing keyboard navigation and screen-reader announcements for the data points.
  • Token contrast: --chart-1 through --chart-5 ship with WCAG AA contrast against the card background in both light and dark themes.
  • Container element: the wrapper renders a ChartContainer div with the series colors applied as CSS variables.
  • No streaming UI: charts do not implement a streaming append affordance. For live counts, throttle parent updates.
  • LineChart - ordered domain when shape matters more than discrete comparison.
  • AreaChart - filled magnitude over an ordered domain.
  • ScatterChart - independent X plus Y when both axes are numeric.

On this page