Bubble Chart

Beautiful charts. Built using chartjs and react-chartjs-2. Copy and paste into your apps or run the command into your CLI.


1. Install utilities

You need to install some necessary packages to use the below Bubble Charts. Please run the command into your terminal.

bun add chart.js react-chartjs-2

2. Setup files

Create a file named as chart-utils.ts inside your lib folder and paste the code.

"use client";

import { useTheme } from "next-themes";
import { useEffect, useState } from "react";

export type ChartColors = {
  primary: string;
  secondary: string;
  tertiary: string;
  quaternary: string;
  background: string;
  grid: string;
  text: string;
};

export function useChartColors(): ChartColors {
  const { theme } = useTheme();
  const [colors, setColors] = useState<ChartColors>({
    primary: "rgb(99, 102, 241)",
    secondary: "rgb(168, 85, 247)",
    tertiary: "rgb(249, 115, 22)",
    quaternary: "rgb(34, 197, 94)",
    background: "rgb(255, 255, 255)",
    grid: "rgb(243, 244, 246)",
    text: "rgb(17, 24, 39)",
  });

  useEffect(() => {
    if (theme === "dark") {
      setColors({
        primary: "rgb(129, 140, 248)",
        secondary: "rgb(192, 132, 252)",
        tertiary: "rgb(251, 146, 60)",
        quaternary: "rgb(74, 222, 128)",
        background: "rgb(17, 24, 39)",
        grid: "rgb(55, 65, 81)",
        text: "rgb(243, 244, 246)",
      });
    } else {
      setColors({
        primary: "rgb(99, 102, 241)",
        secondary: "rgb(168, 85, 247)",
        tertiary: "rgb(249, 115, 22)",
        quaternary: "rgb(34, 197, 94)",
        background: "rgb(255, 255, 255)",
        grid: "rgb(243, 244, 246)",
        text: "rgb(17, 24, 39)",
      });
    }
  }, [theme]);

  return colors;
}

export const generateBubbleData = (
  count: number,
  xMax = 100,
  yMax = 100,
  rMax = 20
) => {
  return Array.from({ length: count }, () => ({
    x: Math.floor(Math.random() * xMax),
    y: Math.floor(Math.random() * yMax),
    r: Math.floor(Math.random() * rMax + 5),
  }));
};

export const generateMultiSeriesBubbleData = (
  series: number,
  pointsPerSeries: number
) => {
  return Array.from({ length: series }, (_, seriesIndex) => ({
    label: `Series ${seriesIndex + 1}`,
    data: Array.from({ length: pointsPerSeries }, () => ({
      x: Math.floor(Math.random() * 100),
      y: Math.floor(Math.random() * 100),
      r: Math.floor(Math.random() * 20 + 5),
      value: Math.floor(Math.random() * 1000 + 100),
    })),
  }));
};

export const generateGeospatialBubbleData = () => {
  const cities = [
    { name: "New York", lat: 40.7128, lng: -74.006, population: 8419000 },
    { name: "Los Angeles", lat: 34.0522, lng: -118.2437, population: 3980000 },
    { name: "Chicago", lat: 41.8781, lng: -87.6298, population: 2716000 },
    { name: "Houston", lat: 29.7604, lng: -95.3698, population: 2328000 },
    { name: "Phoenix", lat: 33.4484, lng: -112.074, population: 1690000 },
    { name: "Philadelphia", lat: 39.9526, lng: -75.1652, population: 1584000 },
    { name: "San Antonio", lat: 29.4241, lng: -98.4936, population: 1547000 },
    { name: "San Diego", lat: 32.7157, lng: -117.1611, population: 1423000 },
    { name: "Dallas", lat: 32.7767, lng: -96.797, population: 1343000 },
    { name: "San Jose", lat: 37.3382, lng: -121.8863, population: 1035000 },
  ];

  return cities.map((city) => ({
    x: city.lng + 180, // Normalize longitude to positive values
    y: city.lat + 90, // Normalize latitude to positive values
    r: Math.sqrt(city.population / 100000), // Scale population to reasonable bubble size
    name: city.name,
    population: city.population,
    lat: city.lat,
    lng: city.lng,
  }));
};

export const generatePackedBubbleData = (count: number) => {
  const categories = [
    "Technology",
    "Healthcare",
    "Finance",
    "Education",
    "Retail",
  ];
  return Array.from({ length: count }, (_, index) => ({
    x: Math.floor(Math.random() * 100),
    y: Math.floor(Math.random() * 100),
    r: Math.floor(Math.random() * 25 + 5),
    category: categories[index % categories.length],
    value: Math.floor(Math.random() * 1000 + 100),
    id: index,
  }));
};

export const generateAnimatedBubbleData = (frame: number, count: number) => {
  return Array.from({ length: count }, (_, index) => {
    const baseX = (index * 20) % 100;
    const baseY = (index * 15) % 100;
    const offset = Math.sin((frame + index) * 0.1) * 10;

    return {
      x: baseX + offset,
      y: baseY + Math.cos((frame + index) * 0.08) * 8,
      r: 8 + Math.sin((frame + index) * 0.15) * 5,
      id: index,
    };
  });
};

Thats it. You are ready to go!


Standard Bubble Chart

Live Preview
Open in

Label Bubble Chart

Live Preview
Open in

Multi-Series Bubble Chart

Live Preview
Open in
Multi-Series Analysis
Series 1
12 bubbles
Avg size: 14
Total: $6.9k
Series 2
12 bubbles
Avg size: 13
Total: $8.6k
Series 3
12 bubbles
Avg size: 16
Total: $7.3k
Series 4
12 bubbles
Avg size: 14
Total: $7.7k

Geospatial Bubble Chart

Live Preview
Open in
Geographic Population Distribution
1M - 2M
2M - 3M
3M - 5M
5M+
New York
8.4M
Los Angeles
4.0M
Chicago
2.7M
Houston
2.3M
Phoenix
1.7M
Philadelphia
1.6M
San Antonio
1.5M
San Diego
1.4M
Dallas
1.3M
San Jose
1.0M

Cartogram Bubble Chart

Live Preview
Open in
Regional Cartogram Visualization
North
15M
$45k
South
25M
$35k
East
30M
$50k
West
20M
$40k
Central
35M
$60k
Northeast
12M
$38k
Southeast
18M
$42k
Southwest
22M
$36k
Northwest
16M
$44k
Cartogram Explanation:
Population Bubbles: Size represents population density, color intensity shows relative population size.
GDP Bubbles: Size represents GDP per capita, positioned near population centers with slight offset.

Packed Bubble Chart

Live Preview
Open in
Circle Packing Visualization
Technology (5)
Healthcare (5)
Finance (5)
Education (5)
Retail (5)
Technology
5 bubbles
Avg: 18
$3.9k
Healthcare
5 bubbles
Avg: 23
$2.7k
Finance
5 bubbles
Avg: 18
$2.3k
Education
5 bubbles
Avg: 15
$2.3k
Retail
5 bubbles
Avg: 18
$3.7k

Interactive Bubble Chart

Live Preview
Open in
to25
Selected: 0 / 26 bubbles
Total
30
Filtered
26
Selected
0
Zoom
1.0x
Size Range
5-25
Color Mode
gradient
💡 Click bubbles to select, use controls to interact

On this page