M
MetricUI
Charts

BulletChart

Bullet chart for comparing actual values against targets with qualitative range bands.

import { BulletChart } from "metricui";

Use BulletChart to compare actual performance against targets with qualitative range bands. Ideal for OKR scorecards, KPI targets, and quota tracking. Supports full BulletDatum format and a simpleData shorthand, horizontal and vertical layouts, configurable range colors, and theme-aware styling. For single-value progress, use Gauge or KpiCard instead.

Basic Example

Pass an array of bullet data with ranges, measures, and markers to render a bullet chart.

Revenue vs Target
<BulletChart
  data={[
    {
      id: "Revenue",
      ranges: [50000, 80000, 100000],
      measures: [72000],
      markers: [85000],
    },
  ]}
  title="Revenue vs Target"
  format="currency"
  height={120}
/>

Simple Data Format

Use simpleDatafor the common "value vs target" case. Ranges are auto-generated from zone percentages (default: [60, 80, 100] of max).

Team Scorecard
<BulletChart
  simpleData={[
    { label: "Revenue", value: 72000, target: 85000 },
    { label: "Users", value: 680, target: 750 },
    { label: "NPS", value: 74, target: 80, max: 100 },
  ]}
  title="Team Scorecard"
/>

Multiple Metrics

Pass multiple items in the data array to compare several KPIs side by side. Height auto-calculates from item count.

Q4 Performance
<BulletChart
  data={[
    { id: "Revenue", ranges: [50000, 80000, 100000], measures: [72000], markers: [85000] },
    { id: "Users", ranges: [500, 800, 1000], measures: [680], markers: [750] },
    { id: "NPS", ranges: [30, 60, 100], measures: [74], markers: [80] },
  ]}
  title="Q4 Performance"
/>

Vertical Layout

Set layout="vertical" for a column-oriented bullet chart. Useful when vertical space is plentiful and you want to read values top-to-bottom.

Vertical Bullets
<BulletChart
  data={[
    { id: "Revenue", ranges: [50000, 80000, 100000], measures: [72000], markers: [85000] },
    { id: "Users", ranges: [500, 800, 1000], measures: [680], markers: [750] },
  ]}
  layout="vertical"
  title="Vertical Bullets"
  height={300}
/>

Custom Styling

Customize measureSize, markerSize, spacing, and titlePosition to tune the visual density and layout.

SaaS Metrics

Actuals vs targets

<BulletChart
  simpleData={[
    { label: "MRR", value: 85000, target: 100000, max: 120000 },
    { label: "NPS Score", value: 72, target: 80, max: 100 },
    { label: "Response Time", value: 120, target: 100, max: 200 },
  ]}
  title="SaaS Metrics"
  subtitle="Actuals vs targets"
  measureSize={0.3}
  markerSize={0.8}
  spacing={32}
  titlePosition="after"
/>

Data States

Every component handles loading, empty, and error states. Pass individual props or use the grouped state prop.

Loading

Error

Failed to load data

// Loading state
<BulletChart data={[]} title="Revenue" loading />

// Error state
<BulletChart data={[]} title="Revenue" error={{ message: "Failed to load" }} />

Props

PropTypeDescription
data
BulletDatum[]

Full bullet data with id, title, ranges, measures, markers.

simpleData
SimpleBulletData[]

Simple shorthand format: label, value, target, max, zones. data takes precedence.

title
string

Chart card title.

subtitle
string

Chart card subtitle.

description
string | React.ReactNode

Description popover.

footnote
string

Footnote.

action
React.ReactNode

Action slot.

format
FormatOption

Format for values in tooltips.

height
number

Height in px. Default: auto-calculated from item count.

layout
"horizontal" | "vertical"

Layout direction.

spacing
number

Gap between bullet items in px.

rangeColors
string[]

Range color scheme. Default: theme-aware greens.

measureColors
string[]

Measure (bar) color scheme. Default: theme accent.

markerColors
string[]

Marker color scheme. Default: theme foreground.

measureSize
number

Size of the measure bar relative to the range (0-1).

markerSize
number

Size of the marker relative to the range height.

titlePosition
"before" | "after"

Title position relative to the bullet.

showAxis
boolean

Show axis.

animate
boolean

Enable/disable animation.

variant
CardVariant

Card variant.

dense
boolean

Compact layout.

className
string

Additional CSS class names.

classNames
{ root?: string; header?: string; chart?: string }

Sub-element class name overrides.

id
string

HTML id attribute.

data-testid
string

Test id.

loading
boolean

Loading state.

empty
EmptyState

Empty state.

error
ErrorState

Error state.

stale
StaleState

Stale indicator.

Data Shape

interface BulletDatum {
  id: string;
  title?: React.ReactNode;
  ranges: number[];      // Cumulative endpoints: [150, 225, 300]
  measures: number[];    // Actual value bars
  markers?: number[];    // Target marker lines
}

interface SimpleBulletData {
  label: string;
  value: number;
  target?: number;
  max?: number;          // Default: auto from target or value * 1.2
  zones?: number[];      // Percentages of max. Default: [60, 80, 100]
}

Notes

  • Uses forwardRef.
  • Uses forwardRef — attach a ref to the root div.
  • simpleData auto-generates ranges from zone percentages (default: [60, 80, 100]). data takes precedence when non-empty.
  • Height auto-calculates from item count when not specified.
  • Range colors are theme-aware (light green shades in light mode, dark green shades in dark mode).
  • Marker lines represent targets; measures are the actual value bars.
  • The tooltip uses ChartTooltip and respects the format prop.