The BubbleChart component visualizes three variables by mapping data to x/y positions and bubble size, ideal for displaying multi-dimensional datasets.
<script lang="ts" setup>
defineOptions({
tags: ['bubblecharts', 'basic']
})
const bubbleChartData = [
{
id: 'Drama-Jan',
title: 'Drama',
month: 1,
viewingHours: 2.8,
subscribers: 45
},
{
id: 'Drama-Feb',
title: 'Drama',
month: 2,
viewingHours: 2.9,
subscribers: 48
},
{
id: 'Drama-Mar',
title: 'Drama',
month: 3,
viewingHours: 3.1,
subscribers: 52
},
{
id: 'Drama-Apr',
title: 'Drama',
month: 4,
viewingHours: 2.7,
subscribers: 46
},
{
id: 'Drama-May',
title: 'Drama',
month: 5,
viewingHours: 3.2,
subscribers: 55
},
{
id: 'Drama-Jun',
title: 'Drama',
month: 6,
viewingHours: 3.0,
subscribers: 50
},
{
id: 'Drama-Jul',
title: 'Drama',
month: 7,
viewingHours: 3.3,
subscribers: 58
},
{
id: 'Drama-Aug',
title: 'Drama',
month: 8,
viewingHours: 3.1,
subscribers: 53
},
{
id: 'Drama-Sep',
title: 'Drama',
month: 9,
viewingHours: 2.9,
subscribers: 49
},
{
id: 'Drama-Oct',
title: 'Drama',
month: 10,
viewingHours: 3.4,
subscribers: 60
},
{
id: 'Drama-Nov',
title: 'Drama',
month: 11,
viewingHours: 3.5,
subscribers: 62
},
{
id: 'Drama-Dec',
title: 'Drama',
month: 12,
viewingHours: 3.6,
subscribers: 65
},
// Action/Thriller
{
id: 'Action-Jan',
title: 'Action/Thriller',
month: 1,
viewingHours: 2.5,
subscribers: 42
},
{
id: 'Action-Feb',
title: 'Action/Thriller',
month: 2,
viewingHours: 2.6,
subscribers: 44
},
{
id: 'Action-Mar',
title: 'Action/Thriller',
month: 3,
viewingHours: 2.8,
subscribers: 48
},
{
id: 'Action-Apr',
title: 'Action/Thriller',
month: 4,
viewingHours: 2.4,
subscribers: 40
},
{
id: 'Action-May',
title: 'Action/Thriller',
month: 5,
viewingHours: 2.9,
subscribers: 50
},
{
id: 'Action-Jun',
title: 'Action/Thriller',
month: 6,
viewingHours: 2.7,
subscribers: 46
},
{
id: 'Action-Jul',
title: 'Action/Thriller',
month: 7,
viewingHours: 3.0,
subscribers: 52
},
{
id: 'Action-Aug',
title: 'Action/Thriller',
month: 8,
viewingHours: 2.8,
subscribers: 48
},
{
id: 'Action-Sep',
title: 'Action/Thriller',
month: 9,
viewingHours: 2.6,
subscribers: 44
},
{
id: 'Action-Oct',
title: 'Action/Thriller',
month: 10,
viewingHours: 3.1,
subscribers: 54
},
{
id: 'Action-Nov',
title: 'Action/Thriller',
month: 11,
viewingHours: 3.2,
subscribers: 56
},
{
id: 'Action-Dec',
title: 'Action/Thriller',
month: 12,
viewingHours: 3.3,
subscribers: 58
},
// Comedy
{
id: 'Comedy-Jan',
title: 'Comedy',
month: 1,
viewingHours: 2.2,
subscribers: 38
},
{
id: 'Comedy-Feb',
title: 'Comedy',
month: 2,
viewingHours: 2.3,
subscribers: 40
},
{
id: 'Comedy-Mar',
title: 'Comedy',
month: 3,
viewingHours: 2.5,
subscribers: 43
},
{
id: 'Comedy-Apr',
title: 'Comedy',
month: 4,
viewingHours: 2.1,
subscribers: 36
},
{
id: 'Comedy-May',
title: 'Comedy',
month: 5,
viewingHours: 2.6,
subscribers: 45
},
{
id: 'Comedy-Jun',
title: 'Comedy',
month: 6,
viewingHours: 2.4,
subscribers: 42
},
{
id: 'Comedy-Jul',
title: 'Comedy',
month: 7,
viewingHours: 2.7,
subscribers: 47
},
{
id: 'Comedy-Aug',
title: 'Comedy',
month: 8,
viewingHours: 2.5,
subscribers: 44
},
{
id: 'Comedy-Sep',
title: 'Comedy',
month: 9,
viewingHours: 2.3,
subscribers: 40
},
{
id: 'Comedy-Oct',
title: 'Comedy',
month: 10,
viewingHours: 2.8,
subscribers: 49
},
{
id: 'Comedy-Nov',
title: 'Comedy',
month: 11,
viewingHours: 2.9,
subscribers: 51
},
{
id: 'Comedy-Dec',
title: 'Comedy',
month: 12,
viewingHours: 3.0,
subscribers: 53
},
// Documentary
{
id: 'Doc-Jan',
title: 'Documentary',
month: 1,
viewingHours: 1.8,
subscribers: 30
},
{
id: 'Doc-Feb',
title: 'Documentary',
month: 2,
viewingHours: 1.9,
subscribers: 32
},
{
id: 'Doc-Mar',
title: 'Documentary',
month: 3,
viewingHours: 2.1,
subscribers: 35
},
{
id: 'Doc-Apr',
title: 'Documentary',
month: 4,
viewingHours: 1.7,
subscribers: 28
},
{
id: 'Doc-May',
title: 'Documentary',
month: 5,
viewingHours: 2.2,
subscribers: 37
},
{
id: 'Doc-Jun',
title: 'Documentary',
month: 6,
viewingHours: 2.0,
subscribers: 34
},
{
id: 'Doc-Jul',
title: 'Documentary',
month: 7,
viewingHours: 2.3,
subscribers: 39
},
{
id: 'Doc-Aug',
title: 'Documentary',
month: 8,
viewingHours: 2.1,
subscribers: 36
},
{
id: 'Doc-Sep',
title: 'Documentary',
month: 9,
viewingHours: 1.9,
subscribers: 32
},
{
id: 'Doc-Oct',
title: 'Documentary',
month: 10,
viewingHours: 2.4,
subscribers: 41
},
{
id: 'Doc-Nov',
title: 'Documentary',
month: 11,
viewingHours: 2.5,
subscribers: 43
},
{
id: 'Doc-Dec',
title: 'Documentary',
month: 12,
viewingHours: 2.6,
subscribers: 45
},
// Romance
{
id: 'Romance-Jan',
title: 'Romance',
month: 1,
viewingHours: 1.9,
subscribers: 32
},
{
id: 'Romance-Feb',
title: 'Romance',
month: 2,
viewingHours: 2.4,
subscribers: 41
},
{
id: 'Romance-Mar',
title: 'Romance',
month: 3,
viewingHours: 2.2,
subscribers: 38
},
{
id: 'Romance-Apr',
title: 'Romance',
month: 4,
viewingHours: 1.8,
subscribers: 30
},
{
id: 'Romance-May',
title: 'Romance',
month: 5,
viewingHours: 2.3,
subscribers: 40
},
{
id: 'Romance-Jun',
title: 'Romance',
month: 6,
viewingHours: 2.1,
subscribers: 36
},
{
id: 'Romance-Jul',
title: 'Romance',
month: 7,
viewingHours: 2.4,
subscribers: 42
},
{
id: 'Romance-Aug',
title: 'Romance',
month: 8,
viewingHours: 2.2,
subscribers: 38
},
{
id: 'Romance-Sep',
title: 'Romance',
month: 9,
viewingHours: 2.0,
subscribers: 34
},
{
id: 'Romance-Oct',
title: 'Romance',
month: 10,
viewingHours: 2.5,
subscribers: 44
},
{
id: 'Romance-Nov',
title: 'Romance',
month: 11,
viewingHours: 2.6,
subscribers: 46
},
{
id: 'Romance-Dec',
title: 'Romance',
month: 12,
viewingHours: 2.7,
subscribers: 48
},
// Sci-Fi/Fantasy
{
id: 'SciFi-Jan',
title: 'Sci-Fi/Fantasy',
month: 1,
viewingHours: 2.1,
subscribers: 35
},
{
id: 'SciFi-Feb',
title: 'Sci-Fi/Fantasy',
month: 2,
viewingHours: 2.2,
subscribers: 37
},
{
id: 'SciFi-Mar',
title: 'Sci-Fi/Fantasy',
month: 3,
viewingHours: 2.4,
subscribers: 41
},
{
id: 'SciFi-Apr',
title: 'Sci-Fi/Fantasy',
month: 4,
viewingHours: 2.0,
subscribers: 33
},
{
id: 'SciFi-May',
title: 'Sci-Fi/Fantasy',
month: 5,
viewingHours: 2.5,
subscribers: 43
},
{
id: 'SciFi-Jun',
title: 'Sci-Fi/Fantasy',
month: 6,
viewingHours: 2.3,
subscribers: 39
},
{
id: 'SciFi-Jul',
title: 'Sci-Fi/Fantasy',
month: 7,
viewingHours: 2.6,
subscribers: 45
},
{
id: 'SciFi-Aug',
title: 'Sci-Fi/Fantasy',
month: 8,
viewingHours: 2.4,
subscribers: 42
},
{
id: 'SciFi-Sep',
title: 'Sci-Fi/Fantasy',
month: 9,
viewingHours: 2.2,
subscribers: 38
},
{
id: 'SciFi-Oct',
title: 'Sci-Fi/Fantasy',
month: 10,
viewingHours: 2.7,
subscribers: 47
},
{
id: 'SciFi-Nov',
title: 'Sci-Fi/Fantasy',
month: 11,
viewingHours: 2.8,
subscribers: 49
},
{
id: 'SciFi-Dec',
title: 'Sci-Fi/Fantasy',
month: 12,
viewingHours: 2.9,
subscribers: 51
},
// Horror
{
id: 'Horror-Jan',
title: 'Horror',
month: 1,
viewingHours: 1.5,
subscribers: 25
},
{
id: 'Horror-Feb',
title: 'Horror',
month: 2,
viewingHours: 1.6,
subscribers: 27
},
{
id: 'Horror-Mar',
title: 'Horror',
month: 3,
viewingHours: 1.7,
subscribers: 29
},
{
id: 'Horror-Apr',
title: 'Horror',
month: 4,
viewingHours: 1.4,
subscribers: 23
},
{
id: 'Horror-May',
title: 'Horror',
month: 5,
viewingHours: 1.8,
subscribers: 30
},
{
id: 'Horror-Jun',
title: 'Horror',
month: 6,
viewingHours: 1.6,
subscribers: 27
},
{
id: 'Horror-Jul',
title: 'Horror',
month: 7,
viewingHours: 1.9,
subscribers: 32
},
{
id: 'Horror-Aug',
title: 'Horror',
month: 8,
viewingHours: 1.7,
subscribers: 29
},
{
id: 'Horror-Sep',
title: 'Horror',
month: 9,
viewingHours: 1.6,
subscribers: 27
},
{
id: 'Horror-Oct',
title: 'Horror',
month: 10,
viewingHours: 2.3,
subscribers: 39
},
{
id: 'Horror-Nov',
title: 'Horror',
month: 11,
viewingHours: 2.0,
subscribers: 34
},
{
id: 'Horror-Dec',
title: 'Horror',
month: 12,
viewingHours: 1.8,
subscribers: 30
}
]
const categories1 = {
'Drama': { name: 'Drama', color: 'var(--color-red-400)' },
'Action/Thriller': {
name: 'Action/Thriller',
color: 'var(--color-orange-400)'
},
'Comedy': { name: 'Comedy', color: 'var(--color-yellow-400)' },
'Documentary': { name: 'Documentary', color: 'var(--color-blue-400)' },
'Romance': { name: 'Romance', color: 'var(--color-pink-400)' },
'Sci-Fi/Fantasy': {
name: 'Sci-Fi/Fantasy',
color: 'var(--color-green-400)'
},
'Horror': { name: 'Horror', color: 'var(--color-purple-400)' }
}
const monthNames = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
function formatNumber(tick: number | Date): string {
return typeof tick === 'number' ? tick.toFixed(1) : String(tick)
}
const xAccessor1 = (d: any) => d.month
const yAccessor1 = (d: any) => d.viewingHours
const sizeAccessor1 = (d: any) => d.subscribers
</script>
<template>
<BubbleChart
:data="bubbleChartData"
:height="230"
:categories="categories1"
category-key="title"
:x-accessor="xAccessor1"
:y-accessor="yAccessor1"
:y-domain-line="false"
:size-accessor="sizeAccessor1"
:legend-position="LegendPosition.BottomRight"
:x-num-ticks="12"
:x-formatter="(tick: number) => monthNames[tick - 1] ?? String(tick)"
:y-formatter="(v: number | Date) => `${formatNumber(v)}B hrs`"
/>
</template>
| Prop | Type | Default | Description |
|---|---|---|---|
data | T[] | Required | Array of data points for the chart. Each element represents a data point. |
height | number | Required | Height of the chart in pixels. |
categories | Record<string, BulletLegendItemInterface> | Required | Series configuration: name and color for each data key. Each unique value in your data's categoryKey field must have a corresponding entry with a color. |
categoryKey | keyof T | Required | Key to access the category from the data item. |
xAccessor | NumericAccessor<T> | undefined | Accessor for x value (index, property, etc). If not provided, defaults to d.beakLength. |
yAccessor | NumericAccessor<T> | undefined | Accessor for y value. If not provided, defaults to d.flipperLength. |
sizeAccessor | NumericAccessor<T> | undefined | Accessor for bubble size. If not provided, defaults to 1. |
xLabel | string | undefined | Optional label for the x-axis. |
yLabel | string | undefined | Optional label for the y-axis. |
padding | { top: number; right: number; bottom: number; left: number; } | undefined | Optional padding for the chart (top, right, bottom, left). |
xFormatter | axisFormatter | undefined | Formats X-axis labels. (tick, i, ticks) => string where tick can be a number or Date. |
yFormatter | axisFormatter | undefined | Formats Y-axis labels. (tick, i, ticks) => string where tick can be a number or Date. |
labelPosition | Position | Position.Bottom | Label position for bubbles. |
sizeRange | [number, number] | [1, 20] | Range for bubble sizes. |
opacity | number | undefined | Opacity for bubbles. |
xExplicitTicks | `(number | string | Date)` |
minMaxTicksOnly | boolean | false | Only show min/max ticks for x axis. |
xNumTicks | number | undefined | Desired number of ticks on the x-axis. |
yNumTicks | number | undefined | Desired number of ticks on the y-axis. |
hideLegend | boolean | false | If true, hides the chart legend. |
hideTooltip | boolean | false | If true, hides the tooltip. |
legendPosition | LegendPosition | undefined | Position of the legend (see LegendPosition). |
legendStyle | Record<string, string> | undefined | Optional inline CSS styles for the legend container. |
sizeOptions | SizeOptions | undefined | Options for controlling bubble sizes (minRadius, maxRadius). |
xDomainLine | boolean | false | Show domain (axis) line on the x-axis. |
yDomainLine | boolean | false | Show domain (axis) line on the y-axis. |
xTickLine | boolean | false | Show tick lines on the x-axis. |
yTickLine | boolean | false | Show tick lines on the y-axis. |
xGridLine | boolean | false | Show grid lines on the x-axis. |
yGridLine | boolean | false | Show grid lines on the y-axis. |
hideXAxis | boolean | false | If true, hides the x-axis. |
hideYAxis | boolean | false | If true, hides the y-axis. |
crosshairConfig | CrosshairConfig | undefined | Crosshair configuration for customizing the crosshair line. |
xAxisConfig | AxisConfig | undefined | Axis configuration for customizing the appearance of the x-axis. |
yAxisConfig | AxisConfig | undefined | Axis configuration for customizing the appearance of the y-axis. |
tooltipTitleFormatter | (data: T) => string | number | undefined | Custom formatter for tooltip titles. |
tooltip | TooltipConfig | undefined | Tooltip configuration (hideDelay, showDelay, followCursor). |
duration | number | undefined | Animation duration in milliseconds. |
The data for your Vue charts should be an array of objects where each object represents a bubble.
interface BubbleChartData {
id: string
title: string
month: number
viewingHours: number
subscribers: number
[key: string]: any
}
Categories define the visual appearance and metadata for each bubble series.
interface BubbleCategory {
name: string
color: string
}
interface BubbleCategories {
[key: string]: BubbleCategory
}