Bubble Chart React component
A bubble chart for three measures at once: x, y and bubble size, coloured by group, split into four labelled quadrants like a portfolio matrix, with bubbles that pop in largest first, names inside the big ones and a tooltip that says which quadrant an item sits in.
About the Bubble Chart component
Bubble Chart shows three measures for every item: position on x and y, and a bubble whose area follows a third value such as revenue or headcount. Groups get their own colour, and optional quadrant lines turn it into a portfolio matrix with a label for each corner.
Bubbles pop in largest first, the big ones carry their name, and hovering, tapping or using the arrow keys shows the full numbers with the quadrant the item falls in.
When to use it
Use it for product or market portfolios (growth vs margin vs revenue), account health (usage vs satisfaction vs ARR), prioritisation (impact vs effort vs reach) and competitor maps.
Keep it to roughly 5–30 bubbles; with many more they overlap. When size doesn't matter, a scatter plot is cleaner.
Installation
Add the Bubble Chart component to your project with the shadcn CLI. It lands in components/bubble-chart.tsx as plain React + Tailwind CSS source you own and can edit.
npx shadcn@latest add @reactframe/bubble-chartOr copy the source by hand and install its dependencies:
npm install clsx tailwind-mergeProps
<BubbleChart /> accepts 14 props, all optional. It also accepts the usual <div> attributes, like className, style, id and aria-*, and passes them to its root element.
| Prop | Type | Default |
|---|---|---|
| theme | "dark" | "light" | "dark" |
| title | string | "Product portfolio" |
| subtitle | string | "Revenue growth vs operating margin, bubble size is annual revenue" |
| groups | { label: string; color?: string }[] | DEFAULT_GROUPS |
| bubbles | { label: string; x: number; y: number; size: number; group: number }[] | DEFAULT_BUBBLES |
| xLabel | string | "Revenue growth" |
| yLabel | string | "Operating margin" |
| sizeLabel | string | "Revenue" |
| quadrants Split the plot into four labelled quadrants; false for none. | { x?: number; y?: number; labels: [string, string, string, string] } | false | DEFAULT_QUADRANTS |
| maxRadius Radius of the largest bubble, in px (scaled down on narrow screens). | number | 46 |
| height Height of the plot area, in px. | number | 340 |
| formatX | (value: number) => string | (v) => `${v > 0 ? "+" : ""}${v}%` |
- theme"dark" | "light"Default: "dark"
- titlestringDefault: "Product portfolio"
- subtitlestringDefault: "Revenue growth vs operating margin, bubble size is annual revenue"
- groups{ label: string; color?: string }[]Default: DEFAULT_GROUPS
- bubbles{ label: string; x: number; y: number; size: number; group: number }[]Default: DEFAULT_BUBBLES
- xLabelstringDefault: "Revenue growth"
- yLabelstringDefault: "Operating margin"
- sizeLabelstringDefault: "Revenue"
- quadrants
Split the plot into four labelled quadrants; false for none.
{ x?: number; y?: number; labels: [string, string, string, string] } | falseDefault: DEFAULT_QUADRANTS - maxRadius
Radius of the largest bubble, in px (scaled down on narrow screens).
numberDefault: 46 - height
Height of the plot area, in px.
numberDefault: 340 - formatX(value: number) => stringDefault: (v) => `${v > 0 ? "+" : ""}${v}%`
Show all 14 propsShow fewer
| formatY | (value: number) => string | (v) => `${v}%` |
| formatSize | (value: number) => string | (v) => `${money.format(v)}M` |
- formatY(value: number) => stringDefault: (v) => `${v}%`
- formatSize(value: number) => stringDefault: (v) => `${money.format(v)}M`
npm dependencies
Installed for you by the shadcn CLI.
Bubble Chart FAQ
- How is bubble size worked out?
- Bubble area, not radius, follows size, so a bubble with twice the value looks twice as big. maxRadius sets the largest bubble; it also shrinks on narrow screens.
- Can I remove the quadrants?
- Pass quadrants={false}. Otherwise give the dividing x and y values (defaults are the middle of each axis) and four labels: top-left, top-right, bottom-left, bottom-right.
- How do I format the numbers?
- formatX, formatY and formatSize format the axes, header and tooltip; xLabel, yLabel and sizeLabel name the measures.
- Is it accessible?
- The plot is focusable with a text summary listing every item; arrow keys cycle through bubbles and Escape clears. With reduced motion the bubbles appear without popping in.