Creating a Donut Chart in React Native with D3 and ART

If you are building a React Native app that displays individualized data to each user, you may want to build dynamic data visualizations. D3 is one of the most popular and powerful libraries for generating dynamic visualizations using JavaScript. It can be integrated with React Native using ART, a library that processes and displays SVG, Scalable Vector Graphics.

In this tutorial, I’ll demonstrate how to build a simple React Native donut chart using D3 and ART. The final product will intake an arbitrary amount of data and produce a donut chart similar to the following:


Setup

In order to use D3 with React Native ART, you first need to link your project with the ART library. React ships with ART, but does not include all of its dependencies due to size considerations.

If you haven’t already, globally install the React Native CLI:

npm install -g react-native-cli

Then, install ART and D3, and run the command to link ART with React Native:

npm install art
npm install d3
react-native link

In your project, create a new file called Donut.js and import the following:

import React from 'react'
import * as d3 from 'd3'
import { ART } from 'react-native'
const { Surface, Group, Shape } = ART

Creating a Surface

To start, we will set up a Surface. This ART component will render the SVG path data created by D3.

Unlike D3 rendered in a web browser, D3 in React Native does not have access to a DOM, and therefore cannot produce visual representations by injecting <svg> elements into other elements. The ART Surface component bridges this gap by creating a space for D3 SVG data to be rendered.

const width = 250
const height = 250
<Surface width={width} height={height}>
//Donut to be inserted here
</Surface>

Donut Chart Data

I will use the following data to create the donut chart; feel free to use any other data, as long as it’s in an array.

const userPurchases = [
{
itemName: 'Mountain Dew',
price: 3
},
{
itemName: 'Shoes',
price: 50
},
{
itemName: 'Kit Kat',
price: 1
},
{
itemName: 'Taxi',
price: 24
},
{
itemName: 'Watch',
price: 100
},
{
itemName: 'Headphones',
price: 15
},
{
itemName: 'Wine',
price: 16
}
]

Generate Chart Angles and Path with D3

In order to plot a donut chart, we need to map each element in our dataset to angles on a circle. We will use the d3.pie() method to get angles for each slice of the donut chart. Since I want to plot the price of each item in the userPurchases dataset above, I will tell D3 to look for the price property via the value method. The d parameter in the value method represents each element of userPurchases:

const sectionAngles = d3.pie().value(d => d.price)(userPurchases)

The output for each of the elements should look like the following:

[...
{ data:
{ itemName: 'Mountain Dew',
price: 3 },
index: 15,
value: 3,
startAngle: 6.219718788925247,
endAngle: 6.267318677616002,
padAngle: 0 }
...]

The original data is still there, but is nested in a data property. Several new properties that contain angle information have been added.

Next, we need to generate an SVG path that we can pass to our Surface component. We will do this via the d3.arc() function, which creates an SVG path based on the chart’s height and width.

const path = d3.arc()
.outerRadius(100) //must be less than 1/2 the chart's height/width
.padAngle(.05) //defines the amount of whitespace between sections
.innerRadius.(60) //the size of the inner 'donut' whitespace

Mapping over the Data to Create a Chart

We now have the angles for each of the elements of our data, and a path function that can map these angles to a circular path with a specific radius. The donut chart will be produced by mapping over the angles with the path function.

Before we do this, we should set up a Group component to group the individual sections of the chart together:

<Surface width={width} height={height}>
<Group x={width/2} y={height/2}>
//Chart sections to go here
</Group>
</Surface>

Notice that the x and y attributes for the Group component are our previously-defined width and height divided by two. We need to specify these coordinates because paths created via d3.arc() are centered by default on coordinates (0,0). If our donut chart is centered on (0, 0), only a quarter of it will be visible. By specifying the x and y coordinates of the Group component to be width/2 and height/2 respectively, we are centering the donut chart in the center of our Surface component.

Now we can map over the angle data to render a donut chart:

<Surface width={width} height={height}>
<Group x={width/2} y={height/2}>
{
sectionAngles.map(section => (
<Shape
key={section.index}
d={path(section)}
stroke="#000"
fill={`rgb(0,0,255)`}
strokeWidth={1}
/>
))
}
</Group>
</Surface>

Colors

One easy way to experiment with colors is to create a color scale that maps each element of the userPurchases data to a value between 0 and 255:

const colors = d3.scaleLinear()
.domain([0, props.userPurchases.length]).range([0, 255])

The fill for each section of the donut chart can then be calculated by passing its index to the colors function:

fill={`rgb(${50},${colors(index) / 1.5},${colors(index)})`}

Next Steps

You should now have a basic donut chart in your React Native app. If you are interested in adding data labels, I would recommend checking out the d3 centroid method.

I hope that this tutorial helped you integrate D3 into your React Native app!