Create SVG line chart in React
Today, we’ll create a very simple SVG chart in React, without external libraries.
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.