ScatterChart
Shows numeric relationships and bubble magnitude across one or more series.
← All chart guides · Framework setup · Native counterparts
See it and copy it
Framework example
Explore the live chart, then open its code for Astro, React, or Elements.
Engagement and conversion
X: weekly active users (%) · Y: trial conversion (%)
3 series, 22 points. Values range from 12 to 84.
- Self-serve
- Guided onboarding
- Partner-led
View chart data
| X | Series | Value | Size |
|---|---|---|---|
| 18 | Self-serve | 12 | 44 |
| 27 | Self-serve | 21 | 72 |
| 34 | Self-serve | 18 | 56 |
| 42 | Self-serve | 32 | 96 |
| 51 | Self-serve | 28 | 64 |
| 58 | Self-serve | 43 | 82 |
| 66 | Self-serve | 38 | 48 |
| 74 | Self-serve | 52 | 60 |
| 24 | Guided onboarding | 29 | 32 |
| 36 | Guided onboarding | 42 | 56 |
| 44 | Guided onboarding | 38 | 44 |
| 53 | Guided onboarding | 55 | 74 |
| 62 | Guided onboarding | 51 | 92 |
| 69 | Guided onboarding | 67 | 62 |
| 78 | Guided onboarding | 62 | 48 |
| 86 | Guided onboarding | 78 | 36 |
| 31 | Partner-led | 34 | 24 |
| 46 | Partner-led | 48 | 38 |
| 57 | Partner-led | 63 | 52 |
| 67 | Partner-led | 58 | 66 |
| 76 | Partner-led | 76 | 46 |
| 91 | Partner-led | 84 | 30 |
View ScatterChart code
Framework usage
Switch targets to compare the adapter code. This live preview runs the Astro example. React and Elements use their own behavior APIs; complete the matching framework setup before copying an example.---
import { ScatterChart } from '@santi020k/lumen-astro'
import type { LumenChartSeries } from '@santi020k/lumen-core'
const series = [
{
id: 'self-serve',
label: 'Self-serve',
tone: 'series-1',
data: [
{ x: 18, y: 12, size: 44 },
{ x: 27, y: 21, size: 72 },
{ x: 34, y: 18, size: 56 },
{ x: 42, y: 32, size: 96 },
{ x: 51, y: 28, size: 64 },
{ x: 58, y: 43, size: 82 },
{ x: 66, y: 38, size: 48 },
{ x: 74, y: 52, size: 60 }
]
},
{
id: 'guided',
label: 'Guided onboarding',
tone: 'series-2',
data: [
{ x: 24, y: 29, size: 32 },
{ x: 36, y: 42, size: 56 },
{ x: 44, y: 38, size: 44 },
{ x: 53, y: 55, size: 74 },
{ x: 62, y: 51, size: 92 },
{ x: 69, y: 67, size: 62 },
{ x: 78, y: 62, size: 48 },
{ x: 86, y: 78, size: 36 }
]
},
{
id: 'partner',
label: 'Partner-led',
tone: 'series-3',
data: [
{ x: 31, y: 34, size: 24 },
{ x: 46, y: 48, size: 38 },
{ x: 57, y: 63, size: 52 },
{ x: 67, y: 58, size: 66 },
{ x: 76, y: 76, size: 46 },
{ x: 91, y: 84, size: 30 }
]
}
] satisfies readonly LumenChartSeries[]
---
<ScatterChart
aria-label="Trial conversion by weekly product engagement and onboarding channel"
heading="Engagement and conversion"
description="X: weekly active users (%) · Y: trial conversion (%)"
caption="September 2026 · Each bubble is a trial cohort; size represents 24–96 teams. Illustrative data compares three onboarding channels."
{series}
/>import { ScatterChart } from '@santi020k/lumen-react'
import type { LumenChartSeries } from '@santi020k/lumen-core'
const series = [
{
id: 'self-serve',
label: 'Self-serve',
tone: 'series-1',
data: [
{ x: 18, y: 12, size: 44 },
{ x: 27, y: 21, size: 72 },
{ x: 34, y: 18, size: 56 },
{ x: 42, y: 32, size: 96 },
{ x: 51, y: 28, size: 64 },
{ x: 58, y: 43, size: 82 },
{ x: 66, y: 38, size: 48 },
{ x: 74, y: 52, size: 60 }
]
},
{
id: 'guided',
label: 'Guided onboarding',
tone: 'series-2',
data: [
{ x: 24, y: 29, size: 32 },
{ x: 36, y: 42, size: 56 },
{ x: 44, y: 38, size: 44 },
{ x: 53, y: 55, size: 74 },
{ x: 62, y: 51, size: 92 },
{ x: 69, y: 67, size: 62 },
{ x: 78, y: 62, size: 48 },
{ x: 86, y: 78, size: 36 }
]
},
{
id: 'partner',
label: 'Partner-led',
tone: 'series-3',
data: [
{ x: 31, y: 34, size: 24 },
{ x: 46, y: 48, size: 38 },
{ x: 57, y: 63, size: 52 },
{ x: 67, y: 58, size: 66 },
{ x: 76, y: 76, size: 46 },
{ x: 91, y: 84, size: 30 }
]
}
] satisfies readonly LumenChartSeries[]
export const Example = () => (
<>
<ScatterChart
aria-label="Trial conversion by weekly product engagement and onboarding channel"
heading="Engagement and conversion"
description="X: weekly active users (%) · Y: trial conversion (%)"
caption="September 2026 · Each bubble is a trial cohort; size represents 24–96 teams. Illustrative data compares three onboarding channels."
series={series}
/>
</>
)<script type="module">
import { defineLumenElements } from '@santi020k/lumen-elements/define'
defineLumenElements(['ScatterChart'])
</script>
<lumen-scatter-chart id="example-scatter-chart"
aria-label="Trial conversion by weekly product engagement and onboarding channel"
heading="Engagement and conversion"
description="X: weekly active users (%) · Y: trial conversion (%)"
caption="September 2026 · Each bubble is a trial cohort; size represents 24–96 teams. Illustrative data compares three onboarding channels."></lumen-scatter-chart>
<script type="module">
const series = [
{
id: 'self-serve',
label: 'Self-serve',
tone: 'series-1',
data: [
{ x: 18, y: 12, size: 44 },
{ x: 27, y: 21, size: 72 },
{ x: 34, y: 18, size: 56 },
{ x: 42, y: 32, size: 96 },
{ x: 51, y: 28, size: 64 },
{ x: 58, y: 43, size: 82 },
{ x: 66, y: 38, size: 48 },
{ x: 74, y: 52, size: 60 }
]
},
{
id: 'guided',
label: 'Guided onboarding',
tone: 'series-2',
data: [
{ x: 24, y: 29, size: 32 },
{ x: 36, y: 42, size: 56 },
{ x: 44, y: 38, size: 44 },
{ x: 53, y: 55, size: 74 },
{ x: 62, y: 51, size: 92 },
{ x: 69, y: 67, size: 62 },
{ x: 78, y: 62, size: 48 },
{ x: 86, y: 78, size: 36 }
]
},
{
id: 'partner',
label: 'Partner-led',
tone: 'series-3',
data: [
{ x: 31, y: 34, size: 24 },
{ x: 46, y: 48, size: 38 },
{ x: 57, y: 63, size: 52 },
{ x: 67, y: 58, size: 66 },
{ x: 76, y: 76, size: 46 },
{ x: 91, y: 84, size: 30 }
]
}
];
export {};
const chart = document.getElementById('example-scatter-chart')
chart?.setAttribute('series', JSON.stringify(series))
</script>Framework differences
Elements: Pass series as a JSON attribute or through the public element property. The Elements example serializes the sample data with JSON.stringify; Astro expression bindings are not HTML attributes.
When to use ScatterChart
Use to explore relationships, clusters, and outliers across observations with both X and Y measurements.
- Point position shows the X/Y pair. When size is supplied, explain what bubble magnitude represents in the caption.
- Use labeled references for meaningful lines or regions, such as a threshold. The data table retains both coordinates and supplied size.
Prepare the data
import type { LumenChartSeries } from '@santi020k/lumen-core'
const series = [{
id: 'teams',
label: 'Teams',
data: [
{ id: 'starter', label: 'Starter teams', x: 12, y: 24, size: 18 },
{ id: 'growing', label: 'Growing teams', x: 38, y: 57, size: 32 }
]
}] satisfies readonly LumenChartSeries[]- Use finite numeric X values for the default linear scale, or timestamps/ISO dates for xScale="time". Logarithmic X values must be positive.
- Y measurements are finite numbers or null. Optional size values must be finite and nonnegative; use labels to identify observations.
- domain controls Y and xDomain controls X. formatX and formatY let each axis use its own measurement unit.
Avoid misleading comparisons
- Association does not establish causation. State the population and relevant sampling limits.
- Overlapping points can hide observations. Inspect exact data and consider grouping or filtering in the application when the plot becomes dense.
Choose your target
Add this component
Choose the package for your runtime. All adapters share the Lumen stylesheet. Use the matching registry command when you want a local wrapper for that framework.
pnpm add @santi020k/lumen-astropnpm add @santi020k/lumen-astrolumen add ScatterChartlumen add ScatterChartpnpm add @santi020k/lumen-reactpnpm add @santi020k/lumen-reactlumen add ScatterChart --target reactlumen add ScatterChart --target reactpnpm add @santi020k/lumen-elementspnpm add @santi020k/lumen-elementslumen add ScatterChart --target elementslumen add ScatterChart --target elementsAPI reference
Lumen-specific props and runtime attributes for the Astro primitive. Use the framework differences above and the copyable examples for React composition and Elements attributes; the public adapter types define their supported contracts.
| Attribute | Values | Default | Description |
|---|---|---|---|
| drilldown | boolean (Astro / Elements attribute) | false | Enables plotted datum actions and an equivalent native button disclosure, including with hidden tables. Astro requires UIPrimitives; Elements owns its lifecycle. |
| onDatumActivate | (detail: LumenChartDatumActivationDetail) => void (React) | - | Enables React datum actions and receives validated values with raw identities. Navigation and filtering remain application owned. |
| explore-data-label, datum-action-prefix, datumActionFormatter | string attributes, (context: string) => string property (Elements) | English labels | Localizes the Elements disclosure and datum actions. |
| labels.exploreData, labels.formatDatumAction | string, (context: string) => string | English labels | Localizes the actions disclosure and each contextual datum button. |
| formatX, formatY | formatter callbacks | String, formatValue | Formats independent units without altering numeric geometry. |
| xDomain, domain | Partial<LumenChartDomain> | derived | Provides explicit X and Y bounds; log X requires positive increasing values. |
| references | readonly LumenScatterReference[] | [] | Draws labeled reference lines or regions using the same scales as points. |
| series | LumenChartSeries[] | [] | Provides x/y points with optional labels and bubble sizes. |
| xScale | "linear" | "time" | "log" | "linear" | Selects the numeric x-axis scale used to position points. |
| formatValue | (value: number) => string | String | Formats point values and bubble sizes in titles and the accessible table. |
| showLegend | boolean | true for multiple series | Shows the color-independent series key. |
| showTable | boolean | true | Provides a revealable semantic table containing every point. |
| class, className | string | "" | Merges custom classes with the generated ui-* root classes. |
| ...native attributes | HTML attributes | - | Forwards standard attributes to the root element unless the component consumes them. |
Keyboard interactions
Keyboard behavior provided by the Astro UIPrimitives runtime for this component.
| Key | Action |
|---|---|
| Tab | Focus the actions disclosure and its native buttons in document order. |
| Enter, Space | Toggle the disclosure or activate the focused datum button. Actions remain available when the data table is hidden. |
Runtime events
CustomEvents dispatched by the Astro UIPrimitives runtime for this component.
| Event | Target | Detail | When |
|---|---|---|---|
| ui:chart-datum-activate | Astro chart figure or Elements data chart host | LumenChartDatumActivationDetail: series { seriesId, x, y, datumId? }, heatmap { x, y, value, datumId? }, range { x, low, high, datumId? } | Fires once after a plotted datum or its native action button is activated. Requires drilldown. Astro uses UIPrimitives, Elements owns its lifecycle, and React uses onDatumActivate instead. |