AdminKitDocs
Charts

Next.js Bubble Chart

A Next.js bubble chart for the App Router: an ApexCharts client component in AdminKit PRO that plots x, y and size per point, in light and dark mode.

Pro

This component is only available in AdminKit PRO. Learn more.

A bubble chart compares items on three measures at once. Each bubble sits at its x and y values, and a third value sets its size. AdminKit for Next.js draws it with ApexChart, a client component around ApexCharts (Chart setup).

In the App Router, your page is a server component. It renders on the server and cannot pass a function to a client component. The chart's options are a function, so put the chart in a client component file, one that starts with "use client", and render that from your page.

Until the browser has drawn the chart, the wrapper shows a skeleton. The static HTML of the page contains that skeleton too.

Example

src/components/bubble-chart.tsx
"use client";

import { ApexChart } from "@/components/plugins/apexcharts";

const random = (min: number, max: number) =>
  Math.floor(Math.random() * (max - min + 1)) + min;

// [x, y, size], generated once when the module loads
const series = ["Bubble1", "Bubble2", "Bubble3", "Bubble4"].map((name) => ({
  name,
  data: Array.from({ length: 20 }, () => [
    random(1, 750),
    random(10, 60),
    random(15, 75),
  ]),
}));

export function BubbleChart() {
  return (
    <div className="position-relative" style={{ minHeight: 365 }}>
      <ApexChart
        options={() => ({
          series,
          chart: { height: 350, type: "bubble" },
          dataLabels: { enabled: false },
          fill: { opacity: 0.8 },
          title: { text: "Simple Bubble Chart" },
          xaxis: { tickAmount: 12, type: "category" },
          yaxis: { max: 70 },
        })}
      />
    </div>
  );
}
src/app/(dashboard)/customers/page.tsx
import { BubbleChart } from "@/components/bubble-chart";
import { pageMetadata } from "@/lib/site";

export const metadata = pageMetadata("/customers");

export default function CustomersPage() {
  return (
    <div className="card">
      <div className="card-body">
        <BubbleChart />
      </div>
    </div>
  );
}

pageMetadata() takes the title and description of /customers from src/data/pages.json, so add the route there.

Props

PropTypeDefaultDescription
options(theme: Theme) => ApexOptionsThe ApexCharts options, with series and chart.type. Called again with the new colors when the color mode changes

Random data and hydration

The module generates the random data once, outside options. The wrapper calls options again when the color mode changes, and data made inside it would move every bubble.

The server render and the browser get different random numbers. That still causes no hydration mismatch, where the server HTML differs from what the browser renders when it takes over the page. The server only renders the skeleton, and the browser draws the chart.

Real data does not have this concern. Pass it from the page as props, as arrays of numbers, or fetch it in the client component.

Data

Each point is [x, y, z], and z sets the bubble size relative to the other bubbles. The series take the primary, success, warning and danger colors of the wrapper. fill.opacity: 0.8 lets overlapping bubbles show through.

Accessibility

  • The values only show in tooltips on hover, and bubble sizes are hard to compare. Give the figures that matter in a table too.
  • Name the chart: other props of ApexChart go to its <div>, such as role="figure" and aria-label.
  • The legend lists the series names next to their colors.

Last updated on

On this page