Create bubble graph with React, d3.js & Typescript
Tags: react, d3, beginners, tutorial
Published: 2023-05-24 16:11:46

### Prerequisites:

To follow along with this blog, you should have a basic understanding of JavaScript, React & Typescript. Familiarity with HTML and CSS will also be helpful.

{% codesandbox charming-leakey-b6z59i  %}


## Why use d3.js

If you're just looking to create a bubble graph, you might not need to use d3.js. Instead, you could try using other libraries like Chart.js, which can make things a bit easier for you. However, if you want to add some interactivity to your graph, such as displaying information when hovering over the bubbles or moving them around, then d3.js might be the way to go.

## What you can expect

This blog is divided into multiple parts. This is the first part, in which we will focus on drawing a basic bubble graph. In the upcoming blogs, we will add various forms of interactivity to it.

- Part 1: Draw a simple bubble graph
- Part 2 (adding interactivity): Display bubble information on mouse hover
- Part 3 (adding interactivity): Move bubbles using drag and drop

## Drawing basic bubble graph
Lets break down it multiple sub-tasks first
1. Set up the project
2. Get the data ready
3. Create the bubble chart component
4. Draw bubbles with d3.js
5. Use that bubble chart component

## **Step 1: Set up the Project**

Assuming you have already installed node environment in your machine, proceed to create a new React project with TypeScript support. Install the required packages for d3.js and open the project in vs-code.

```bash
# create project
npx create-react-app bubble-chart --template typescript

# install d3 package or dependencies
npm install d3 @types/d3

# open project in vs-code
cd bubble-chart
code . 
```

## **Step 2: Prepare mock Data**

We will be working with dummy data. The dataset will be an array of objects, each representing a circle (bubble) in the graph. Each object will have these properties

```jsx
export type BubbleData = {
  name: string;
  x: number;
  y: number;
  size: number;

  // add more data if required
};

export const dummyData: BubbleData[] = [
  { name: "Bubble1", x: 10, y: 50, size: 10 },
  { name: "Bubble2", x: 20, y: 60, size: 20 },
  { name: "Bubble3", x: 15, y: 45, size: 15 },
  { name: "Bubble4", x: 25, y: 70, size: 25 },
  // add more data if required
];
```

## **Step 3: Create the Bubble Chart Component**

Let's create a React component called **BubbleChart**. We will use d3 to manipulate an **SVG** component that we can access through the **ref hook** defined in this component.

```jsx
import React, { useRef, useEffect } from 'react';
import * as d3 from 'd3';

interface Props {
	data: BubbleData[]
}

const BubbleChart = ({ data } : Props) => {
    const ref = useRef();

    useEffect(() => {
        // d3 code will go here
    }, [data]);

    return (
        <svg ref={ref}></svg>
    );
};

export default BubbleChart;
```

We're also passing **`data`** as a prop to **`BubbleChart`** and listening for changes to re-render the chart.

## **Step 4: Implementing the Chart with d3.js**

Let's define the d3.js bubble layout within the **`useEffect`** hook:

```jsx
useEffect(() => {
    const svg = d3.select(ref.current);

    // set svg height and width
    const width = 500;
    const height = 500;
    svg.attr('width', width).attr('height', height);

    // create scales to map all x, y points to the svg viewport
    const xScale = d3.scaleLinear()
      .domain([0, d3.max(data, d => d.x)!])
      .range([0, width]);

    const yScale = d3.scaleLinear()
      .domain([0, d3.max(data, d => d.y)!])
      .range([height, 0]);

    // now draw bubbles
    const bubble = svg.selectAll('circle')
      .data(data)
      .enter()
      .append('circle')
      .attr('cx', d => xScale(d.x))
      .attr('cy', d => yScale(d.y))
      .attr('r', d => d.size)
      .attr('fill', 'blue')
      .attr('stroke', 'black')
      .attr('stroke-width', 2);
  }, [data]);
```

In the updated **`useEffect`** hook, we're following these steps:

1. **Selecting the SVG element:** Using **`d3.select`**, we get a reference to the div where our SVG will be placed (**`ref.current`**).
2. **Setting SVG dimensions:** We set the width and height of the SVG canvas to the desired size using **`.attr`**.
3. **Creating scales:** The **`xScale`** and **`yScale`** functions map the range of our data to pixels on the screen. The **`scaleLinear`** function is used because our **`x`** and **`y`** data are continuous linear values. The **`domain`** is the range of input data, and the **`range`** is the range of the output plot.
    
    **`d3.max(data, d => d.x)`** and **`d3.max(data, d => d.y)`** give us the maximum **`x`** and **`y`** value in our data respectively. These are used to set the maximum value of the domain for each scale.
    
    The **`range`** for **`xScale`** is straightforward (from 0 to the width of the SVG), but for **`yScale`**, it's inverted. This is because in SVG, the y-coordinate starts from the top. Hence, to make sure higher values appear at the top of the chart, we set the range as [height, 0].
    
4. **Drawing the bubbles:** Using **`selectAll('circle')`** we select all future circle elements, then bind our data to these elements with **`.data(data)`**.
    
    The **`enter`** function returns a placeholder for each data point that doesn't yet have a corresponding DOM element. Then, for each of these placeholders, we append a 'circle' to the SVG.
    
    For each circle, we set the center of the circle ('cx' and 'cy' attributes) according to the data's **`x`** and **`y`** values, applying the scales we created earlier. The radius ('r') of the circle is determined by the data's **`size`** value. The 'fill' color, 'stroke' color, and 'stroke-width' are set to static values, but they could also be dynamically set based on the data. 
5. **Listening for data changes:** The second argument to **`useEffect`** is **`[data]`**, meaning the effect will rerun whenever the **`data`** prop changes. This ensures that the chart will update when new data is passed in, allowing for dynamic updates of the visualization.

This is a fundamental example of how you can use d3.js with React to create a bubble chart. By understanding these basics, you can customize and add to your charts as per your requirements. For example, you might want to add different colors for different categories of data, animate the bubbles, or add more interactivity. The possibilities with d3.js and React are endless.

## **Step 5: Using the BubbleChart Component**

Now, you can use the **`BubbleChart`** component in your application. In your **`App.js`** file, import the **`BubbleChart`** component and the data:

```jsx
import React from 'react';
import BubbleChart from './components/BubbleChart';
import { dummyData } from "./graph/data";

function App() {
  return (
    <div className="App">
      <BubbleChart data={dummyData} />
    </div>
  );
}

export default App;

```

Run your app and you should see the bubble chart displayed!

```bash
npm start
```

Congratulations! You have created an interactive bubble chart using d3.js and React. This is a fundamental example, and d3.js allows for much more complexity and customization if needed. Remember that the most effective visualizations are those that successfully convey the underlying data to the audience in a meaningful way. Happy coding!

### Reference

- [MDN circle documentation](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle)
- [D3.js](https://d3js.org)
- [Basic bubble plot](https://d3-graph-gallery.com/graph/bubble_basic.html)