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.
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.
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.
We’ll use data from React to create SVG based chart.
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 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!
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 empty array, the data will be passed.
color defaults to orangered
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;
}
Before we can graph the data, we need to know the minimum and maximum values in thee 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.
It’ll create svg coordinates for each poinr 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.
We need to make 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 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.
The chart is created but it doesn’t have 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.
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.
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.
Originally published at React Ninja.
4.7/5 Stars || 33.5 Hours of Video || 61,597 Students
Learn React or dive deeper into it. Learn the theory, solve assignments, practice in demo projects and build one big application which is improved throughout the course: The Burger Builder! Learn More.
React 16 - The Complete Guide (incl. React Router 4 & Redux)
Build Complete Web and Hybrid Mobile Solutions. Master front-end web, hybrid mobile app and server-side development in four comprehensive courses with Coursera Enroll to start your 7-day full access free trial.
Full-Stack Web Development with React | Coursera
I publish articles on React, React Native, and everything else related to web development on React Ninja. Be sure and follow me on Twitter.
Join our Newsletter to get the latest and greatest content to make you a better developer.