Bubble Chart
Extend scatter plots with a third variable encoded by marker size for richer comparisons.
Source
LLM docs
Package
npm install @plocks/chartsExamples
6
Properties
57
Playground
Basics
Scatter-style plot where point radius encodes a third quantitative dimension.
Loading demo…
import { BubbleChart } from '@plocks/charts';
import { companies } from './data';
export function Demo() {
return (
<BubbleChart
title="Revenue vs Growth"
subtitle="Bubble size shows valuation (in millions)"
h={360}
data={companies}
dataKey={{
x: 'revenue',
y: 'growth',
z: 'valuation',
label: 'company',
id: 'company',
}}
xAxis={{
title: 'Annual revenue (USD millions)',
labelFormatter: (value) => `${Math.round(value)}m`,
}}
yAxis={{
title: 'YoY growth %',
labelFormatter: (value) => `${Math.round(value)}%`,
}}
valueFormatter={(value) => `$${Math.round(value)}m`}
grid={{ show: true }}
withTooltip
range={[64, 1152]}
/>
);
}Cities Talent Footprint
Loading demo…
import { BubbleChart } from '@plocks/charts';
import { Region, cities, regionPalette } from './data';
const formatFootprint = (value: number) => `${value.toFixed(0)}k sq ft`;
export function Demo() {
return (
<BubbleChart
title="Global Talent Hubs"
subtitle="Talent depth vs cost of living — bubble size represents active office footprint"
h={440}
data={cities}
dataKey={{
x: 'costOfLivingIndex',
y: 'talentDepth',
z: 'officeFootprintKsqft',
label: 'city',
color: 'region',
id: 'city',
}}
colorScale={(value) => (value && regionPalette[value as Region]) || regionPalette.Americas}
grid={{ show: true }}
xAxis={{
title: 'Cost of living index (base 100 = SF)',
labelFormatter: (value) => value.toFixed(0),
}}
yAxis={{
title: 'Tech talent depth (0–100 readiness)',
labelFormatter: (value) => value.toFixed(0),
}}
valueFormatter={(value) => formatFootprint(value)}
tooltip={{
formatter: ({ record, value }) => [
`Office footprint: ${formatFootprint(value)}`,
`Remote ready: ${record.remoteReady}% • Avg tenure: ${record.averageTenure.toFixed(1)} yrs`,
`Anchor university: ${record.anchorUniversity}`,
].join('\n'),
}}
range={[81, 1764]}
legend={{ show: true, position: 'right', align: 'start' }}
/>
);
}Customer Account Health
Loading demo…
import { BubbleChart } from '@plocks/charts';
import { accounts } from './data';
const formatArr = (value: number) => `$${value.toFixed(2)}M ARR`;
export function Demo() {
return (
<BubbleChart
title="Customer Account Health vs Expansion"
subtitle="Bubble size reflects current ARR; use upper-right quadrant to spot ready-to-expand logos"
h={440}
data={accounts}
dataKey={{
x: 'healthScore',
y: 'expansionPotential',
z: 'arr',
label: 'account',
id: 'account',
}}
xAxis={{
title: 'Account health score',
labelFormatter: (value) => `${Math.round(value)}`,
}}
yAxis={{
title: 'Expansion potential score',
labelFormatter: (value) => `${Math.round(value)}`,
}}
grid={{ show: true }}
valueFormatter={(value) => formatArr(value)}
tooltip={{
formatter: ({ record, value }) => [
formatArr(value),
`Segment: ${record.segment} • CSM: ${record.csOwner}`,
`Last touch: ${record.lastTouch}`,
].join('\n'),
}}
range={[96, 1728]}
/>
);
}Engineering Epic Risk
Loading demo…
import { BubbleChart } from '@plocks/charts';
import { Squad, epics, squadPalette } from './data';
const formatMultiplier = (value: number) => `${value.toFixed(1)}× risk`;
export function Demo() {
return (
<BubbleChart
title="Epic Risk Landscape"
subtitle="Story points vs defect density — bubble area communicates composite risk multiplier"
h={420}
data={epics}
dataKey={{
x: 'storyPoints',
y: 'defectDensity',
z: 'riskMultiplier',
label: 'epic',
color: 'squad',
id: 'epic',
}}
colorScale={(value) => (value ? squadPalette[value as Squad] : squadPalette['Platform Reliability'])}
grid={{ show: true }}
xAxis={{
title: 'Estimated effort (story points)',
labelFormatter: (value) => `${Math.round(value)}`,
}}
yAxis={{
title: 'Defect density (per 1000 lines)',
labelFormatter: (value) => value.toFixed(1),
}}
valueFormatter={(value) => formatMultiplier(value)}
tooltip={{
formatter: ({ record, value }) => [
formatMultiplier(value),
`Critical paths: ${record.criticalPaths}`,
`Squad: ${record.squad} • Phase: ${record.phase}`,
].join('\n'),
}}
range={[64, 1296]}
legend={{ show: true, position: 'right', align: 'start' }}
/>
);
}Product Strategy Portfolio
Loading demo…
import { BubbleChart } from '@plocks/charts';
import { initiatives } from './data';
const formatMillions = (value: number) => `$${value.toFixed(1)}M`;
export function Demo() {
return (
<BubbleChart
title="Product Initiative Portfolio"
subtitle="Strategic value vs execution effort — bubble scales with projected revenue"
h={440}
data={initiatives}
dataKey={{
x: 'executionEffort',
y: 'strategicValue',
z: 'projectedRevenue',
label: 'initiative',
id: 'initiative',
}}
grid={{ show: true }}
xAxis={{
title: 'Execution effort (1=low, 10=high)',
labelFormatter: (value) => value.toFixed(1),
}}
yAxis={{
title: 'Strategic value (1=low, 10=high)',
labelFormatter: (value) => value.toFixed(1),
}}
valueFormatter={(value) => formatMillions(value)}
tooltip={{
formatter: ({ record, value }) => [
`Projected revenue: ${formatMillions(value)}`,
`Confidence: ${record.confidence}%`,
`Owner: ${record.owner} • Horizon: ${record.horizon}`,
].join('\n'),
}}
range={[72, 1440]}
/>
);
}Vendor Contracts Scoring
Loading demo…
import { BubbleChart } from '@plocks/charts';
import { Category, categoryPalette, contracts } from './data';
const formatSpend = (value: number) => `$${value.toFixed(1)}M`;
export function Demo() {
return (
<BubbleChart
title="Vendor Contract Health"
subtitle="Compliance vs renewal probability — bubble area encodes annual spend"
h={420}
data={contracts}
dataKey={{
x: 'complianceScore',
y: 'renewalProbability',
z: 'annualSpendMillions',
label: 'vendor',
color: 'category',
id: 'vendor',
}}
colorScale={(value) => (value && categoryPalette[value as Category]) || categoryPalette.Cloud}
grid={{ show: true }}
xAxis={{
title: 'Compliance readiness score',
labelFormatter: (value) => `${Math.round(value)}`,
}}
yAxis={{
title: 'Renewal probability %',
labelFormatter: (value) => `${Math.round(value)}%`,
}}
valueFormatter={(value) => formatSpend(value)}
tooltip={{
formatter: ({ record, value }) => [
`Annual spend: ${formatSpend(value)}`,
`Owner: ${record.owner} • Term ends: ${record.termEnds}`,
`Risk: ${record.riskLevel}`,
].join('\n'),
}}
range={[72, 1620]}
legend={{ show: true, position: 'right', align: 'start' }}
/>
);
}Related charts
Further reading
React Native design for iOS, Android, and Web.
Quick Links
Resources