Skip to content
design
Components

Bar chart

Compare amounts per category as grouped or stacked bars with a zero baseline and a source table.

On this page

Daily work

Illustrative data · Monday through Friday

  • Planned work
  • Repairs
Items per day · incomplete stacks have no drawn total
Compare amounts per category as grouped or stacked bars with a zero baseline and a source table.
Code
import { BarChart } from "@bosunkit/components/bar-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import type { ChartConfig } from "@bosunkit/components/chart-container";
import type { ReactElement } from "react";

export const description: string =
  "Illustrative category amounts as vertical grouped bars; Wednesday has an unmeasured category.";

interface Amount {
  readonly day: string;
  readonly planned: number | null;
  readonly repairs: number | null;
}
const data: readonly Amount[] = [
  { day: "Mon", planned: 4, repairs: 2 },
  { day: "Tue", planned: 5, repairs: 3 },
  { day: "Wed", planned: 6, repairs: null },
  { day: "Thu", planned: 3, repairs: 2 },
  { day: "Fri", planned: 5, repairs: 1 },
];
const values: Readonly<Record<number, string>> = {
  0: "0",
  1: "1",
  2: "2",
  3: "3",
  4: "4",
  5: "5",
  6: "6",
  7: "7",
  8: "8",
  9: "9",
  10: "10",
};
const writeValue: (value: number) => string = (value: number): string => values[value] ?? "";
const writeDay: (value: string | number) => string = (value: string | number): string =>
  typeof value === "string" ? value : "";
const config: ChartConfig<Amount> = {
  planned: { label: "Planned work", formatValue: writeValue },
  repairs: { label: "Repairs", formatValue: writeValue },
};

export default function DailyAmounts(): ReactElement {
  return (
    <ChartPanel
      title="Daily work"
      description="Illustrative data · Monday through Friday"
      legend={<ChartLegend config={config} mark="bar" label="Work categories" />}
      footer="Items per day · incomplete stacks have no drawn total"
    >
      <BarChart
        data={data}
        categoryKey="day"
        config={config}
        series={["planned", "repairs"]}
        mode="grouped"
        orientation="vertical"
        valueDomain={[0, 10]}
        formatCategory={writeDay}
        formatAxisValue={writeValue}
        label="Illustrative work amounts, Monday through Friday"
        categoryLabel="Day"
        dataTableLabel="View daily amounts"
        missingValueText="Not counted"
      />
    </ChartPanel>
  );
}

A bar chart compares amounts per category. Grouped columns compare series beside one another; stacked columns show composition; horizontal bars give category names more space. The value scale always includes zero, including a caller-supplied domain.

01One series definitionNames, palette slots and patterns come from the same registry used by the drawing.
02Caller-formatted valuesThe caller supplies display text and quantity formatters; missing values have an explicit description.
03Accessible inspectionNames and patterns supplement color, and analytical plots expose their observations in a source table.

Use

An incomplete stack has no drawn total. Its known observations remain in the source table, with caller-supplied text for missing values. Use a shared unit and comparison basis for all series. An explicit value domain clips observations outside its bounds. Filtering preserves the domain of the complete registry. Every series has a label and a patterned swatch; category colors alone do not identify a series.

categoryScale="linear" preserves numeric bin spacing for a histogram. Its categories must be finite and increasing. A numeric categoryDomain controls the viewport; source rows remain available when the drawing clips them.

Examples

Composition

Daily work

Illustrative data · Monday through Friday

  • Planned work
  • Repairs
Items per day · incomplete stacks have no drawn total
Illustrative category amounts as vertical grouped bars; Wednesday has an unmeasured category.
Code
import { BarChart } from "@bosunkit/components/bar-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import type { ChartConfig } from "@bosunkit/components/chart-container";
import type { ReactElement } from "react";

export const description: string =
  "Illustrative category amounts as vertical grouped bars; Wednesday has an unmeasured category.";

interface Amount {
  readonly day: string;
  readonly planned: number | null;
  readonly repairs: number | null;
}
const data: readonly Amount[] = [
  { day: "Mon", planned: 4, repairs: 2 },
  { day: "Tue", planned: 5, repairs: 3 },
  { day: "Wed", planned: 6, repairs: null },
  { day: "Thu", planned: 3, repairs: 2 },
  { day: "Fri", planned: 5, repairs: 1 },
];
const values: Readonly<Record<number, string>> = {
  0: "0",
  1: "1",
  2: "2",
  3: "3",
  4: "4",
  5: "5",
  6: "6",
  7: "7",
  8: "8",
  9: "9",
  10: "10",
};
const writeValue: (value: number) => string = (value: number): string => values[value] ?? "";
const writeDay: (value: string | number) => string = (value: string | number): string =>
  typeof value === "string" ? value : "";
const config: ChartConfig<Amount> = {
  planned: { label: "Planned work", formatValue: writeValue },
  repairs: { label: "Repairs", formatValue: writeValue },
};

export default function DailyAmounts(): ReactElement {
  return (
    <ChartPanel
      title="Daily work"
      description="Illustrative data · Monday through Friday"
      legend={<ChartLegend config={config} mark="bar" label="Work categories" />}
      footer="Items per day · incomplete stacks have no drawn total"
    >
      <BarChart
        data={data}
        categoryKey="day"
        config={config}
        series={["planned", "repairs"]}
        mode="grouped"
        orientation="vertical"
        valueDomain={[0, 10]}
        formatCategory={writeDay}
        formatAxisValue={writeValue}
        label="Illustrative work amounts, Monday through Friday"
        categoryLabel="Day"
        dataTableLabel="View daily amounts"
        missingValueText="Not counted"
      />
    </ChartPanel>
  );
}

Stacked

Daily work

Illustrative data · Monday through Friday

  • Planned work
  • Repairs
Items per day · incomplete stacks have no drawn total
Illustrative category amounts as vertical stacked bars; Wednesday has an unmeasured category.
Code
import { BarChart } from "@bosunkit/components/bar-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import type { ChartConfig } from "@bosunkit/components/chart-container";
import type { ReactElement } from "react";

export const description: string =
  "Illustrative category amounts as vertical stacked bars; Wednesday has an unmeasured category.";

interface Amount {
  readonly day: string;
  readonly planned: number | null;
  readonly repairs: number | null;
}
const data: readonly Amount[] = [
  { day: "Mon", planned: 4, repairs: 2 },
  { day: "Tue", planned: 5, repairs: 3 },
  { day: "Wed", planned: 6, repairs: null },
  { day: "Thu", planned: 3, repairs: 2 },
  { day: "Fri", planned: 5, repairs: 1 },
];
const values: Readonly<Record<number, string>> = {
  0: "0",
  1: "1",
  2: "2",
  3: "3",
  4: "4",
  5: "5",
  6: "6",
  7: "7",
  8: "8",
  9: "9",
  10: "10",
};
const writeValue: (value: number) => string = (value: number): string => values[value] ?? "";
const writeDay: (value: string | number) => string = (value: string | number): string =>
  typeof value === "string" ? value : "";
const config: ChartConfig<Amount> = {
  planned: { label: "Planned work", formatValue: writeValue },
  repairs: { label: "Repairs", formatValue: writeValue },
};

export default function DailyAmounts(): ReactElement {
  return (
    <ChartPanel
      title="Daily work"
      description="Illustrative data · Monday through Friday"
      legend={<ChartLegend config={config} mark="bar" label="Work categories" />}
      footer="Items per day · incomplete stacks have no drawn total"
    >
      <BarChart
        data={data}
        categoryKey="day"
        config={config}
        series={["planned", "repairs"]}
        mode="stacked"
        orientation="vertical"
        valueDomain={[0, 10]}
        formatCategory={writeDay}
        formatAxisValue={writeValue}
        label="Illustrative work amounts, Monday through Friday"
        categoryLabel="Day"
        dataTableLabel="View daily amounts"
        missingValueText="Not counted"
      />
    </ChartPanel>
  );
}

Horizontal

Daily work

Illustrative data · Monday through Friday

  • Planned work
  • Repairs
Items per day · incomplete stacks have no drawn total
Illustrative category amounts as horizontal stacked bars; Wednesday has an unmeasured category.
Code
import { BarChart } from "@bosunkit/components/bar-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import type { ChartConfig } from "@bosunkit/components/chart-container";
import type { ReactElement } from "react";

export const description: string =
  "Illustrative category amounts as horizontal stacked bars; Wednesday has an unmeasured category.";

interface Amount {
  readonly day: string;
  readonly planned: number | null;
  readonly repairs: number | null;
}
const data: readonly Amount[] = [
  { day: "Mon", planned: 4, repairs: 2 },
  { day: "Tue", planned: 5, repairs: 3 },
  { day: "Wed", planned: 6, repairs: null },
  { day: "Thu", planned: 3, repairs: 2 },
  { day: "Fri", planned: 5, repairs: 1 },
];
const values: Readonly<Record<number, string>> = {
  0: "0",
  1: "1",
  2: "2",
  3: "3",
  4: "4",
  5: "5",
  6: "6",
  7: "7",
  8: "8",
  9: "9",
  10: "10",
};
const writeValue: (value: number) => string = (value: number): string => values[value] ?? "";
const writeDay: (value: string | number) => string = (value: string | number): string =>
  typeof value === "string" ? value : "";
const config: ChartConfig<Amount> = {
  planned: { label: "Planned work", formatValue: writeValue },
  repairs: { label: "Repairs", formatValue: writeValue },
};

export default function DailyAmounts(): ReactElement {
  return (
    <ChartPanel
      title="Daily work"
      description="Illustrative data · Monday through Friday"
      legend={<ChartLegend config={config} mark="bar" label="Work categories" />}
      footer="Items per day · incomplete stacks have no drawn total"
    >
      <BarChart
        data={data}
        categoryKey="day"
        config={config}
        series={["planned", "repairs"]}
        mode="stacked"
        orientation="horizontal"
        valueDomain={[0, 10]}
        formatCategory={writeDay}
        formatAxisValue={writeValue}
        label="Illustrative work amounts, Monday through Friday"
        categoryLabel="Day"
        dataTableLabel="View daily amounts"
        missingValueText="Not counted"
      />
    </ChartPanel>
  );
}

Summary callout

Changes per month

Illustrative example · January–June

  • Pull requests
  • Change requests
A persistent summary bubble names the total and reporting period; all observations are illustrative.
Code
import type { ReactElement } from "react";
import { BarChart } from "@bosunkit/components/bar-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
// Fixed illustrative display strings; consumers supply their quantity formatter.
export const description: string =
  "A persistent summary bubble names the total and reporting period; all observations are illustrative.";
const labels: Readonly<Record<number, string>> = {
  "0": "0",
  "100": "100",
  "150": "150",
  "180": "180",
  "200": "200",
  "250": "250",
  "345": "345",
  "900": "900",
  "1000": "1000",
  "1350": "1350",
  "1620": "1620",
  "1800": "1800",
  "2000": "2000",
  "2250": "2250",
  "2700": "2700",
  "3000": "3000",
  "3600": "3600",
  "3112": "3112",
  "4000": "4000",
};
const format: (value: number) => string = (value: number): string => labels[value] ?? "";
const config = {
  pull: { label: "Pull requests", formatValue: format },
  changes: { label: "Change requests", formatValue: format },
};
export default function SummaryBubble(): ReactElement {
  return (
    <ChartPanel
      title="Changes per month"
      description="Illustrative example · January–June"
      legend={<ChartLegend config={config} label="Change categories" mark="bar" />}
    >
      <BarChart
        data={[
          { month: "Jan", pull: 100, changes: 900 },
          { month: "Feb", pull: 150, changes: 1350 },
          { month: "Mar", pull: 180, changes: 1620 },
          { month: "Apr", pull: 200, changes: 1800 },
          { month: "May", pull: 250, changes: 2250 },
          { month: "Jun", pull: 345, changes: 3112 },
        ]}
        categoryKey="month"
        config={config}
        series={["pull", "changes"]}
        mode="stacked"
        formatCategory={(value: string | number): string => String(value)}
        formatAxisValue={format}
        label="Illustrative changes per month"
        categoryLabel="Month"
        dataTableLabel="View monthly changes"
        missingValueText="Unavailable"
        callout={{ value: "12,257", description: "changes since January", position: "top-start" }}
      />
    </ChartPanel>
  );
}

Latency distribution and P95

Equal-width buckets show request share. The percentile marker comes from the underlying requests; bucket centers do not establish the exact P95. Keep the bucket width and denominator visible.

Latency distribution

Share of requests by response time.

Equal-width latency buckets show request share, with an independently supplied P95 at 1.9 seconds.
Code
import { ChartContainer } from "@bosunkit/components/chart-container";
import { ChartTooltip } from "@bosunkit/components/chart-tooltip";
import { DataTable, type Column } from "@bosunkit/components/data-table";
import { Collapsible, CollapsibleTrigger, CollapsiblePanel } from "@bosunkit/components/collapsible";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import * as Recharts from "recharts";
import type { ReactElement } from "react";

export const description: string =
  "Equal-width latency buckets show request share, with an independently supplied P95 at 1.9 seconds.";
const shares: readonly number[] = [2, 8, 16, 25, 20, 13, 8, 4, 1, 0.8, 0.7, 0.6, 0.5, 0.4];
const data = shares.map((share: number, index: number): { center: number; share: number } => ({
  center: index * 250 + 125,
  share,
}));
const shareLabels: Readonly<Record<number, string>> = {
  0: "0%",
  0.1: "0.1%",
  0.2: "0.2%",
  0.4: "0.4%",
  0.5: "0.5%",
  0.6: "0.6%",
  0.7: "0.7%",
  0.8: "0.8%",
  4: "4%",
  8: "8%",
  13: "13%",
  16: "16%",
  25: "25%",
  1: "1%",
  2: "2%",
  3: "3%",
  10: "10%",
  15: "15%",
  20: "20%",
  23: "23%",
  30: "30%",
  40: "40%",
  50: "50%",
};
const bins: readonly string[] = [
  "0 to 0.25 s",
  "0.25 to 0.5 s",
  "0.5 to 0.75 s",
  "0.75 to 1 s",
  "1 to 1.25 s",
  "1.25 to 1.5 s",
  "1.5 to 1.75 s",
  "1.75 to 2 s",
  "2 to 2.25 s",
  "2.25 to 2.5 s",
  "2.5 to 2.75 s",
  "2.75 to 3 s",
  "3 to 3.25 s",
  "3.25 to 3.5 s",
];
const ticks: Readonly<Record<number, string>> = {
  0: "0 s",
  500: "0.5 s",
  1000: "1 s",
  1500: "1.5 s",
  1900: "1.9 s",
  2000: "2 s",
  2500: "2.5 s",
  3000: "3 s",
  3500: "3.5 s",
};
const formatCategory: (value: string | number) => string = (value: string | number): string => {
  if (typeof value !== "number") return "";
  return bins[(value - 125) / 250] ?? ticks[value] ?? "";
};
const formatValue: (value: number) => string = (value: number): string => shareLabels[value] ?? "";
const columns: readonly Column[] = [
  { key: "bucket", label: "Latency bucket" },
  { key: "share", label: "Request share", align: "end" },
];
const sourceRows: readonly Record<string, unknown>[] = data.map((row, index) => ({
  bucket: bins[index],
  share: formatValue(row.share),
}));
export default function LatencyDistribution(): ReactElement {
  return (
    <div>
      <ChartPanel
        appearance="analytical"
        title="Latency distribution"
        description="Share of requests by response time."
        plotMinHeight={0}
      >
        <ChartContainer
          config={{ share: { label: "Request share", formatValue, palette: "analytical", color: "01" } }}
          label="Illustrative latency distribution, Sep 27 to Oct 4, 2026"
          height="var(--bk-analysis-distribution-height)"
          formatCategory={formatCategory}
          missingValueText="Not counted"
        >
          <Recharts.BarChart
            data={data}
            accessibilityLayer
            aria-label="Request share per 250 ms latency bucket"
            barCategoryGap="12%"
          >
            <Recharts.XAxis
              type="number"
              dataKey="center"
              domain={[0, 3500]}
              ticks={[0, 1000, 2000, 3500]}
              tickFormatter={(value: number): string => (value === 3500 ? "3 s+" : (ticks[value] ?? ""))}
              axisLine={false}
              tickLine={false}
            />
            <Recharts.YAxis hide domain={[0, 30]} />
            <Recharts.Bar dataKey="share" isAnimationActive={false} radius={[3, 3, 0, 0]}>
              {data.map((row, index): ReactElement => (
                <Recharts.Cell
                  key={row.center}
                  fill={`var(--data-categorical-analytical-${index < 5 ? "01" : index < 8 ? "03" : "08"})`}
                />
              ))}
            </Recharts.Bar>
            <Recharts.ReferenceLine
              x={1900}
              stroke="var(--bk-analysis-muted)"
              strokeDasharray="3 3"
              label={{
                value: "P95 1.9 s",
                position: "insideTopRight",
                fill: "var(--bk-analysis-text)",
                fontSize: "var(--bk-type-caption-size)",
              }}
            />
            <ChartTooltip />
          </Recharts.BarChart>
        </ChartContainer>
      </ChartPanel>
      <Collapsible>
        <CollapsibleTrigger>View latency distribution</CollapsibleTrigger>
        <CollapsiblePanel motion={false}>
          <p>Illustrative cohort · Sep 27 to Oct 4, 2026 · 250 ms buckets · 100% of requests · supplied P95 1.9 s.</p>
          <DataTable columns={columns} rows={sourceRows} />
        </CollapsiblePanel>
      </Collapsible>
    </div>
  );
}

Error breakdown

A complete horizontal stack shows the composition of failures. Its denominator is failed requests, so these percentages do not describe the overall request error rate. Counts reconcile with the shares. Hovering a segment highlights that segment and shows its failure category, exact count and share. The ranked rows and source table expose the full breakdown without hover.

Error breakdown

Why requests failed this week.

  1. Rate limited1.9k46%
  2. Upstream timeout1.2k28%
  3. Content filtered58814%
  4. Provider 5xx50412%

Rate limits account for 46% of failures in this example.

A thin blue composition bar inspects each failure segment with its count and share; all 4,200 illustrative failures reconcile with the ranked rows.
Code
import { ChartContainer, type ChartRegistry } from "@bosunkit/components/chart-container";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import { ChartTooltip } from "@bosunkit/components/chart-tooltip";
import { Collapsible, CollapsibleTrigger, CollapsiblePanel } from "@bosunkit/components/collapsible";
import { DataTable, type Column } from "@bosunkit/components/data-table";
import * as Recharts from "recharts";
import type { ReactElement } from "react";

export const description: string =
  "A thin blue composition bar inspects each failure segment with its count and share; all 4,200 illustrative failures reconcile with the ranked rows.";
const entries = [
  { key: "limited", label: "Rate limited", count: "1,932", compact: "1.9k", share: 46, shareText: "46%", color: "01" },
  {
    key: "timeout",
    label: "Upstream timeout",
    count: "1,176",
    compact: "1.2k",
    share: 28,
    shareText: "28%",
    color: "02",
  },
  {
    key: "filtered",
    label: "Content filtered",
    count: "588",
    compact: "588",
    share: 14,
    shareText: "14%",
    color: "03",
  },
  { key: "provider", label: "Provider 5xx", count: "504", compact: "504", share: 12, shareText: "12%", color: "08" },
] as const;
const shares: Readonly<Record<number, string>> = { 12: "12%", 14: "14%", 28: "28%", 46: "46%" };
const formatValue: (value: number) => string = (value: number): string => shares[value] ?? "";
const config: ChartRegistry = Object.fromEntries(
  entries.map((entry) => [
    entry.key,
    {
      label: entry.label,
      formatValue: (value: number): string => `${entry.count} requests (${formatValue(value)})`,
      palette: "analytical",
      color: entry.color,
      pattern: "solid",
    },
  ]),
);
const data = [{ cohort: "Failed requests", ...Object.fromEntries(entries.map((entry) => [entry.key, entry.share])) }];
const columns: readonly Column[] = [
  { key: "reason", label: "Failure" },
  { key: "count", label: "Requests", align: "end" },
  { key: "share", label: "Share of failures", align: "end" },
];
const rows: readonly Record<string, unknown>[] = entries.map((entry) => ({
  reason: entry.label,
  count: entry.count,
  share: entry.shareText,
}));
export default function ErrorBreakdown(): ReactElement {
  return (
    <div>
      <ChartPanel
        appearance="analytical"
        title="Error breakdown"
        description="Why requests failed this week."
        plotMinHeight={0}
      >
        <ChartContainer
          config={config}
          label="Illustrative failure composition, Sep 27 to Oct 4, 2026"
          height="var(--bk-analysis-breakdown-height)"
          formatCategory={(value: string | number): string => (typeof value === "string" ? value : "")}
          missingValueText="Not counted"
        >
          <Recharts.BarChart
            data={data}
            layout="vertical"
            accessibilityLayer
            aria-label="Share of failed requests by reason"
            margin={{ top: 0, right: 0, bottom: 0, left: 0 }}
          >
            <Recharts.XAxis type="number" hide domain={[0, 100]} />
            <Recharts.YAxis type="category" dataKey="cohort" hide />
            {entries.map((entry, index): ReactElement => (
              <Recharts.Bar
                key={entry.key}
                dataKey={entry.key}
                name={entry.label}
                stackId="failures"
                fill={`var(--color-${entry.key})`}
                isAnimationActive={false}
                activeBar={{ stroke: "var(--on-flat-active-color)", strokeWidth: "var(--bk-chart-line-width)" }}
                stroke="var(--bk-analysis-panel-surface)"
                strokeWidth={1}
                radius={index === 0 ? [6, 0, 0, 6] : index === entries.length - 1 ? [0, 6, 6, 0] : 0}
              />
            ))}
            <ChartTooltip shared={false} />
          </Recharts.BarChart>
        </ChartContainer>
        <ol
          aria-label="Failure categories and request counts"
          style={{
            listStyle: "none",
            margin: "var(--space-md) 0",
            padding: 0,
            display: "grid",
            gap: "var(--space-md)",
            font: "var(--bk-type-body-reading)",
          }}
        >
          {entries.map((entry, index): ReactElement => (
            <li
              key={entry.key}
              style={{
                display: "grid",
                gridTemplateColumns: "1em 0.5em minmax(0, 1fr) auto auto",
                alignItems: "center",
                gap: "var(--space-sm)",
              }}
            >
              <span aria-hidden="true" style={{ color: "var(--bk-analysis-muted)", font: "var(--bk-type-caption)" }}>
                {["1", "2", "3", "4"][index]}
              </span>
              <span
                aria-hidden="true"
                style={{
                  width: "var(--space-sm)",
                  height: "var(--space-sm)",
                  borderRadius: "var(--radius-xs)",
                  background: `var(--data-categorical-analytical-${entry.color})`,
                }}
              />
              <span>{entry.label}</span>
              <span
                aria-label={`${entry.count} requests`}
                style={{ color: "var(--bk-analysis-muted)", textAlign: "right", minWidth: "3em" }}
              >
                {entry.compact}
              </span>
              <strong style={{ textAlign: "right", minWidth: "3em", fontWeight: 500 }}>{entry.shareText}</strong>
            </li>
          ))}
        </ol>
        <p
          role="note"
          style={{
            margin: "var(--space-xl) 0 0",
            padding: "var(--space-md)",
            border: "1px solid var(--bk-analysis-panel-border)",
            borderRadius: "var(--radius-sm)",
            font: "var(--bk-type-body)",
            color: "var(--bk-analysis-muted)",
          }}
        >
          <span aria-hidden="true" style={{ color: "var(--data-categorical-analytical-01)" }}>
            ●{" "}
          </span>
          Rate limits account for 46% of failures in this example.
        </p>
      </ChartPanel>
      <Collapsible>
        <CollapsibleTrigger>View failure counts and shares</CollapsibleTrigger>
        <CollapsiblePanel motion={false}>
          <p>
            Illustrative cohort · Sep 27 to Oct 4, 2026 · 4,200 failed requests · exclusive categories · 100% of
            failures.
          </p>
          <DataTable columns={columns} rows={rows} />
        </CollapsiblePanel>
      </Collapsible>
    </div>
  );
}

Props

PropTypeDefaultDescription
categoryScale"category" | "linear" | undefined"category"Equal category spacing by default; linear preserves numeric bin or timestamp spacing.
mode"grouped" | "stacked" | undefined"grouped"Group bars or stack them; grouped by default. An incomplete stack is a gap, with its known values preserved in the source table.
orientation"vertical" | "horizontal" | undefined–Vertical columns or horizontal bars; vertical by default.
calloutChartCallout | undefined–A visible summary bubble with caller-formatted value and context.
onCategoryDomainChange((range: readonly [number, number]) => void) | undefined–Receives a numeric viewport after drag-to-zoom or Shift-drag pan; requires categoryDomain.
maxGapnumber | undefined–Maximum elapsed distance connected by a line; numeric categories only.
categoryDomainreadonly [number, number] | undefined–Controlled numeric/time viewport; emits no requests by itself.
syncIdstring | undefined–Charts sharing this identifier synchronize inspection by category value or nearest time.
syncTolerancenumber | undefined–Maximum distance for synchronized numeric categories; zero requires exact matches.
interpolation"linear" | "stepAfter" | undefined–Linear segments or step changes; linear by default. Missing values remain gaps.
describeObservation((row: T) => string) | undefined–Caller-formatted source, processing stage or quality shown alongside every source-table row.
datarequiredreadonly T[]–Rows in display order; numeric time categories must be increasing.
categoryKeyrequiredChartCategoryKey<NoInfer<T>>–The row field naming a category or locating a sample on a numeric axis.
configrequiredChartConfig<NoInfer<T>>–Definitions for numeric series, shared with legends and controls.
seriesrequiredreadonly ChartValueKey<NoInfer<T>>[]–The visible numeric fields, in display order. Filtering preserves the complete registry's domain.
formatCategoryrequired(value: string | number) => string–Writes category ticks, tooltip headings and the first table column.
formatAxisValuerequired(value: number) => string–Writes value-axis ticks; separate from each series' observation formatter.
labelrequiredstring–The drawing's accessible name, naming the quantity and time window.
categoryLabelrequiredstring–The first table column's heading, translated by the caller.
dataTableLabelrequiredstring–The disclosure button and source table's accessible name, translated by the caller.
missingValueTextrequiredstring–Display text for missing values in the tooltip and source table.
heightstring | undefined–The plot height as a CSS length; the chart height token by default.
valueDomainreadonly [number, number] | undefined–An explicit increasing value domain that clips outlying values; bar domains always include zero.
showGridboolean | undefined–Draw the grid; on by default.
showAxesboolean | undefined–Draw axes; on by default. Values remain available in the table when off.
motionMotionProp | undefined–Entrance and exit motion; off by default. Measurements never interpolate.
childrenReactNode | undefined–Recharts annotations appended inside the plot, such as ReferenceLine. Use ChartContainer for a custom root.
classNamestring | undefined–Classes added to the root.
testIdstring | undefined–Becomes data-testid on the root, with --plot and --table parts.
refRef<HTMLDivElement> | undefined–The chart root element.
Do

Daily work

Illustrative data · Monday through Friday

  • Planned work
  • Repairs
Items per day · incomplete stacks have no drawn total
Name the observations and keep the comparison basis visible.
Don't

Keep a gap and describe the missing measurement in the source table.

A missing observation must not appear to be zero.
  • Data visualization: scales, missing measurements and comparable evidence.
  • Chart panel: titles, controls and source information.
  • Sparkline: compact trends with operational integrity and forecast information.