React is the most popular frontend web framework in the world, and D3.js is among the most popular JavaScript libraries to manipulate graphics on-screen.
Version 6 is the latest release of D3.js, and in this article, we’ll look at how to use D3.js v6 in our React apps.
First, we’ll create a React project using create-react-app like so:
npx create-react-app d3-app cd d3-app npm start
Then we install the D3.js package:
npm i d3
Now we can add D3 to our React app to add some graphics.
We can use D3 in our app by putting the D3 code in the useEffect
Hook callback. This is because we’re selecting an element with the DOM and then changing it with D3.
For example, we can write:
import React, { useEffect } from "react"; import * as d3 from "d3"; export default function App() { useEffect(() => { d3.select(".target").style("stroke-width", 5); }, []); return ( <div className="App"> <svg> <circle class="target" style={{ fill: "green" }} stroke="black" cx={50} cy={50} r={40} ></circle> </svg> </div> ); }
We get the SVG with the target
class and then change the stroke-width
after selecting it.
We can also put the D3 code in a function and call it when we’d like to use it. For example, we can write:
import React from "react"; import * as d3 from "d3"; export default function App() { const changeStroke = () => { d3.select(".target").style("stroke-width", 5); }; return ( <div className="App"> <button onClick={changeStroke}>change stroke</button> <svg> <circle class="target" style={{ fill: "green" }} stroke="black" cx={50} cy={50} r={40} ></circle> </svg> </div> ); }
We set the D3 code to change the circle’s stroke in the changeStroke
function, and we call it on a button click.
We can also add everything into another svg
element. For example, we can add three circles by writing the following code:
import React, { useEffect } from "react"; import * as d3 from "d3"; export default function App() { useEffect(() => { const svg = d3.select("#area"); svg .append("circle") .attr("cx", 50) .attr("cy", 50) .attr("r", 40) .style("fill", "blue"); svg .append("circle") .attr("cx", 140) .attr("cy", 70) .attr("r", 40) .style("fill", "red"); svg .append("circle") .attr("cx", 300) .attr("cy", 100) .attr("r", 40) .style("fill", "green"); }, []); return ( <div className="App"> <svg id="area" height={200} width={450}></svg> </div> ); }
Let’s break it down:
append
method with the 'circle'
argument adds the circleattr
method calls add the attributes for the circlecx
is the x-coordinate of the circle’s centercy
is the y-coordinate of the circle’s centerr
is the radiusstyle
contains the properties and values that we want to put into the circle’s style
attributeWe can scale graphics with D3.js in our React app by using the scaleLinear
method. For example, we can write the following code to add an x-axis to our graph:
import React, { useEffect } from "react"; import * as d3 from "d3"; export default function App() { useEffect(() => { const margin = { top: 10, right: 40, bottom: 30, left: 30 }, width = 450 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; const svg = d3 .select("#area") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left}, ${margin.top})`); const x = d3.scaleLinear().domain([0, 100]).range([0, width]); svg .append("g") .attr("transform", `translate(0, ${height})`) .call(d3.axisBottom(x)); const y = d3.scaleLinear().domain([0, 100]).range([height, 0]); svg.append("g").call(d3.axisLeft(y)); }, []); return ( <div className="App"> <svg id="area" height={400} width={500}></svg> </div> ); }
We simply call d3.axisBottom
with our x
function to add the x-axis. It’ll add the values between the min and max values we passed into the domain
function.
It should go without saying that we can add a y-axis to a graph with D3.
In the code above, you’ll notice we added the margin
object to let us set the margins for the graph more easily. Then we append the svh
object to the body of the page with the following code:
const svg = d3 .select("#area") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left}, ${margin.top})`);
We set the width
and height
and then translate
the object into the desired position we want.
Next, we add the x-axis and scale by writing:
const x = d3.scaleLinear().domain([0, 100]).range([0, width]); svg .append("g") .attr("transform", `translate(0, ${height})`) .call(d3.axisBottom(x));
We call the domain
and range
as we did before, and we call the d3.axisBottom
method to add the x-axis.
Then, to add the y-axis, we write:
const y = d3.scaleLinear().domain([0, 100]).range([height, 0]); svg.append("g").call(d3.axisLeft(y));
We called domain
and range
to create the min and max values for the y-axis, then we just call d3.axisLeft
to add the y-axis.
To add dots/points to the graph to create a scatter plot, we can add circles as we did before. For example, we can write the following to create a full scatterplot graph:
import React, { useEffect } from "react"; import * as d3 from "d3"; const DATA = [ { x: 10, y: 20 }, { x: 20, y: 50 }, { x: 80, y: 90 } ]; export default function App() { useEffect(() => { const margin = { top: 10, right: 40, bottom: 30, left: 30 }, width = 450 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; const svg = d3 .select("#area") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left}, ${margin.top})`); const x = d3.scaleLinear().domain([0, 100]).range([0, width]); svg .append("g") .attr("transform", `translate(0, ${height})`) .call(d3.axisBottom(x)); const y = d3.scaleLinear().domain([0, 100]).range([height, 0]); svg.append("g").call(d3.axisLeft(y)); svg .selectAll("whatever") .data(DATA) .enter() .append("circle") .attr("cx", (d) => x(d.x)) .attr("cy", (d) => y(d.y)) .attr("r", 7); }, []); return ( <div className="App"> <svg id="area" height={400} width={500}></svg> </div> ); }
Let’s break it down a bit. You’ll notice we added this code in the useEffect
callback to render the points on the scatterplot:
svg .selectAll("whatever") .data(DATA) .enter() .append("circle") .attr("cx", (d) => x(d.x)) .attr("cy", (d) => y(d.y)) .attr("r", 7);
We read the data with the data
method. The DATA
array is:
const DATA = [ { x: 10, y: 20 }, { x: 20, y: 50 }, { x: 80, y: 90 } ];
Then, to get the x- and y-coordinates relative to the domain, we use the x
and y
methods with the dots. The values are between 0 and 100, and they’ll automatically be scaled proportionally to the width
and height
values.
So if x
is 10, then on the screen, cx
would be 10 * (450 - margin.left - margin.right)
; if y
is 20, then on the screen, cy
would be 20 * (400 - margin.top - margin.bottom)
.
One of the most common use cases for D3 is to create graphs from data. To do so, we can read the data from a CSV.
For example, we can read data from the data.csv
file in the src
folder:
date,close 01-May-20,58.13 30-Apr-20,53.98 27-Apr-20,67.00 26-Apr-20,89.70 25-Apr-20,99.00 24-Apr-20,130.28 23-Apr-20,166.70 20-Apr-20,234.98 19-Apr-20,345.44 18-Apr-20,443.34 17-Apr-20,543.70 16-Apr-20,580.13 13-Apr-20,605.23 12-Apr-20,622.77 11-Apr-20,626.20 10-Apr-20,628.44 09-Apr-20,636.23 05-Apr-20,633.68 04-Apr-20,624.31 03-Apr-20,629.32 02-Apr-20,618.63
In App.js
, we write:
import React, { useEffect } from "react"; import * as d3 from "d3"; import "d3-time-format"; const parseTime = d3.timeParse("%d-%b-%y"); const createGraph = async () => { const margin = { top: 20, right: 20, bottom: 50, left: 70 }, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; const x = d3.scaleTime().range([0, width]); const y = d3.scaleLinear().range([height, 0]); const valueLine = d3.line() .x((d) => { return x(d.date); }) .y((d) => { return y(d.close); }); const svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left}, ${margin.top})`); let data = await d3.csv(require("./data.csv")) data.forEach((d) => { d.date = parseTime(d.date); d.close = +d.close; }); data = data.sort((a, b) => +a.date - +b.date) x.domain(d3.extent(data, (d) => { return d.date; })); y.domain([0, d3.max(data, (d) => { return d.close; })]); svg.append("path") .data([data]) .attr("class", "line") .attr("d", valueLine); svg.append("g") .attr("transform", `translate(0, ${height})`) .call(d3.axisBottom(x)); svg.append("g") .call(d3.axisLeft(y)); } export default function App() { useEffect(() => { createGraph(); }, []); return ( <div> <style>{ ` .line { fill: none; stroke: steelblue; stroke-width: 2px; } `} </style> </div> ); }
We move the graph creation code to the createGraph
function. We’ve also added the d3-time-format
library by running npm i d3-time-format
. Then we can import it like we do on this file.
We created the axes in a similar way to the previous examples. We create the x
and y
functions like so, so that we can create the axes with them later:
const x = d3.scaleTime().range([0, width]); const y = d3.scaleLinear().range([height, 0]);
The container for the graph is created with:
const svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left}, ${margin.top})`);
Then, we read the CSV data with:
let data = await d3.csv(require("./data.csv"))
We parse the time and change the close
property into numbers with:
data.forEach((d) => { d.date = parseTime(d.date); d.close = +d.close; });
Then we sort the data by date like so:
data = data.sort((a, b) => +a.date - +b.date)
We add the line with the following code:
svg.append("path") .data([data]) .attr("class", "line") .attr("d", valueLine);
And we add the x- and y-axes, respectively:
svg.append("g") .attr("transform", `translate(0, ${height})`) .call(d3.axisBottom(x));
svg.append("g") .call(d3.axisLeft(y));
Finally, in the App
component, we write:
<style>{ ` .line { fill: none; stroke: steelblue; stroke-width: 2px; } `}
With this, we’ll see a line instead of a filled shape.
As you’ve just seen, we can easily create graphics with D3.js. It works well with React apps and can be integrated with no extra work.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]