Create SVG line chart in React

Today, we’ll create a very simple SVG chart in React, without external libraries.

Krissanawat Kaewsanmuang
6 min readMar 27, 2019

JavaScript knowledge is required, familiarity with react is expected. It is assumed that you have Node, create-react-app installed. If you don’t have the tools set up, I have gone through them in detail in another tutorial. Follow that and come back.

Here’s how our chart will look like at the end.

Create a react app with

create-react-app reactdev-svg-chart

Go to the directory with

cd reactdev-svg-chart

Start the app with

npm start

It should open a welcome screen on your default browser.

What SVG is?

We’re creating a SVG chart so you must know what SVG is.

SVG stands for Scalable Vector Graphics. It allows to create vector based shapes. Vector graphics are resoultion independent and created on mathematical rules such as line or circle. They load fast, are searchable and modular as well.

How to create a SVG?

Let’s look at a simple SVG:

<path d = "M 50 60 L 100 100 z">

path in the tag for creating svg

d contains list of path instructions

M means Move To.

50 60 is a coordinate. x =50 and y = 60

L means Line To

100 100 is another coordinate.

z means the end or closing

So <path d = "M 50 60 L 100 100 z"> can be explained in plain English as

move to (50, 60) and create a line to (100, 100) than close the SVG element.

This path is included inside the svg tag to render.

That was easy, right? There are more attributes that can be used, refer them at W3Schools.

So what we are doing with SVG on React?

We’ll use data from React to create SVG based chart.

Creating data on React

This is the first task, to create data that will be passed to svg element.

import React, { Component } from 'react'
import './App.css'
class App extends Component {
randomArray = (total = 10) => {
let data = []
for (let element = 0; element < total; element++) {
const y = Math.floor(Math.random() * 50) + 50
const obj = {
x: element,
y,
}
data.push(obj)
}
return data
}
render() {
return (
<div className="App">
{/*Render SVG Chart here!*/}
{/**/}
</div>
)
}
}
export default App

randomArray() creates a random array of objects w=for our purpose.

We need data like: [{x:0, y:54}, {x:1, y:72}, ...]. The y-coordinates will lie between 50 and 100.

We could manually supply this data, but I chose to make it random. The number of random values can be required can be passed as a parameter or it’ll use the default value.

NOTE: The (0, 0) coordinate lies on the left top corner of your screen and (maxX, maxY) lies at the right bottom. Do not get confused!

Line Chart

In the ./src/ create another file LineChart.js. It will contain the LineChart component.

import React, { Component } from 'react'
import './LineChart.css'
class LineChart extends Component {
render() {
return <svg />
}
}
LineChart.defaultProps = {
data: [],
color: '#ff4500',
svgHeight: 200,
svgWidth: 600,
}
export default LineChart

The props:

data an empty array, the data will be passed.

color defaults to orange-red

svgHeight and svgWidth refer to the height and width of the SVG element.

Where is the LineChart.css file?

It’s here, but if you get stuck all code is on this GitHub repository.

.linechart_path {
stroke-width: 2;
fill: none;
}
.linechart_axis {
stroke: #000000;
}

Minimum and Maximum values for SVG!

Before we can graph the data, we need to know the minimum and maximum values in the data.

getMinX and getMinY are the helper functions for this purpose.

getMinX() {
const {data} = this.props
const only_x = data.map(obj => obj.x)
const min_x = Math.min.apply(null, only_x),
return min_x
}
getMinY() {
const { data } = this.props
const only_y = data.map(obj => obj.y)
const min_y = Math.min.apply(null, only_y),
return min_y
}
getMaxX() {
const {data} = this.props
const only_x = data.map(obj => obj.x)
const max_x = Math.max.apply(null, only_x),
return max_x
}
getMaxY() {
const { data } = this.props
const only_y = data.map(obj => obj.y)
const max_y = Math.max.apply(null, only_y),
return max_y
}

Now we have the minimum and maximum x and y coordinates, we also need the SVG coordinates.

SVG Coordinate creator

It’ll create SVG coordinates for each point corresponding to the point in our data.

We need the x and y coordinates so we use two functions.

getSvgX(x){
const { svgWidth } = this.props;
return (x / this.getMaxX() * svgWidth);
}
getSvgY(y) {
const { svgHeight } = this.props;
return svgHeight - (y / this.getMaxY() * svgHeight);
}

x/MaxX * width divides the width of our SVG element uniformly.

Creating a SVG line graph

We need to make a path for each element, we sue another function makePath() for this.

makePath() {
const { data, color } = this.props
let pathD = ` M ${this.getSvgX(data[0].x)} ${this.getSvgY(data[0].y)} `
pathD += data.map((point, i) => {
return `L ${this.getSvgX(point.x)} ${this.getSvgY(point.y)} `
})

It gets data and color from props.

pathD makes it the path move to the first coordinate, this refers to d attribute in the path.

For each value of the coordinate in data, the line from previous to current is returned.

This new line is appended to the previous one.

className is used for styling.

Creating Grids

The chart is created but it doesn’t have a container, so the points in the graph make no sense.

To make sense out of the chart, there must be the axis. The axis is: vertical in the left and horizontal in the bottom.

We make use of another helper function makeAxis() for this.

makeAxis() {
const minX = this.getMinX()
const maxX = this.getMaxX()
const minY = this.getMinY()
const maxY = this.getMaxY()
return (
<g className="linechart_axis">
<line
x1={this.getSvgX(minX)}
y1={this.getSvgY(minY)}
x2={this.getSvgX(maxX)}
y2={this.getSvgY(minY)}
/>
<line
x1={this.getSvgX(minX)}
y1={this.getSvgY(minY)}
x2={this.getSvgX(minX)}
y2={this.getSvgY(maxY)}
/>
</g>
)
}

We get both max and min.

We return two lines in g tag. It is a container tag for SVG, like div for other tags.

getSvgX and getSvgY achieve coordinates for starting and ending.

Plotting Axis and Line Graph

We have everything we need… All we need to do is return makePath and makeAxis.

render() {
const { svgHeight, svgWidth } = this.props
return (
<svg viewBox={`0 0 ${svgWidth} ${svgHeight}`}>
{this.makePath()}
{this.makeAxis()}
</svg>
)
}

The viewbox starts from (0, 0) and goes up to the dimensions we defined.

The Main file

We did everything but update App.js.

import React, { Component } from 'react'
import './App.css'
import LineChart from './LineChart'
class App extends Component {
randomArray = (total = 10) => {
let data = []
for (let element = 0; element < total; element++) {
const y = Math.floor(Math.random() * 50) + 50
const obj = {
x: element,
y,
}
data.push(obj)
}
return data
}
render() {
return (
<div className="App">
<div className="App">
<div className="header">ReactDev SVG Chart</div>
<LineChart data={this.randomArray()} />
</div>
</div>
)
}
}
export default App

Import LineChart and return this component. The data is passed through props from randomArray()!

Your chart won’t look like above as it uses random data every time.

--

--