Custom React Charts
In our journey as software engineers, oftentimes we find ourselves trying to represent data in formats that are easily understandable to end-users. We could be building anything, from a simple application comprised of one or two charts to a complex dashboard with many moving parts - including charts, cards, and even graphs. Depending on the use case, it might be more beneficial to stick to a library that provides robust solutions for all the ways we might intend to display the data. Other times, it might be better to build a custom component in our application that we can use to display simple data.
In this article, we will be exploring how to build simple custom components using CSS and javascript (React) with which we would display information. We will not only explore a few examples but help you understand how to go about creating these components; this article will serve as a blueprint, and as such, we will be able to apply this knowledge in creating other components we desire.
Pie Chart
We'd kick off our implementation of custom CSS charts by building our own pie chart.
First off, we'd create a sample pie chart using just CSS. We'll take advantage of the conic-gradient
CSS function to turn a regular-looking circle into a pie chart. These articles by w3Schools.com and mdn can serve as quick refreshers on what conic-gradient
function is and how it works.
background: conic-gradient(green 0deg 100deg, red 100deg 280deg, blue 280deg);
Here's a snippet of the code we've written so far. We have created a Piechart component and styled it using CSS, wrapped it in a Chart container component, and rendered it
Next, we'll add a bit of dynamism to it which will allow our component to take an object with values and craft a chart with labels. We would make our Piechart component stateful, making it able to maintain the state of the chart's colors and labels.
const ChartContainer = () => {
//The sample data we would be working with
const fruits = {
apples: 10,
oranges: 6,
bananas: 14,
grapes: 12,
pineapples: 3,
berries: 6
}
return (
<div className="chartContainer">
<PieChart fruits={fruits} />
</div>
)
}
//The piechart component takes in a fruits prop
const PieChart = ({fruits}) => {
//we are using a default background of grey for our chart
const [background, setBackground] = React.useState('grey');
const [labels, setLabels] = React.useState([])
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
const pieChartRef = React.useRef(null)
return (
<div className="piechart" ref={pieChartRef}></div>
)
}
The colors array stores values of colors that we will use in our custom chart, you can make use of normal color names, RGB, or HSL values too. Basically, we can make use of anything that is CSS compliant.
The useRef
hook will help us access our piechart DOM element directly, we will take advantage of this later to update the background of our PieChart element.
Next, we would write a useEffect
function that will run once our component mounts, this function will help us process the data, and populate the colors on our background.
React.useEffect(() => {
const paintChart = () => {
//This array will contain the number of fruits
let chartData = []
//This array will contain the names of fruits
let chartLabels = []
//We loop through the fruits object collecting the required data
for (let fruit in fruits){
chartData.push(fruits[fruit])
chartLabels.push(`${fruit}`)
}
const sum = chartData.reduce((x, y) => x + y)
// circle spans 360 degrees, here we convert the values using their ratio to the sum multiplied by 360
chartData = chartData.map((x) => Math.round(360 * x/sum))
let str = ''
let cumulativeDegree = 0
//Here, we loop through the data, creating a string for use in the conic-gradient function
for (let i = 0; i < chartData.length; i++) {
//Check if it's the first data in an array containing more than one data
if(i === 0 && chartData.length > 1){
str += `${colors[i]} 0 ${chartData[i]}deg,`
} else if(i + 1 === chartData.length) { //Check if it's the last data
//Take note of the space between space at the beginning of the string
str += ` ${colors[i]} ${cumulativeDegree}deg ${cumulativeDegree + chartData[i]}deg`
} else {
//Take note of the space between space at the beginning of the string
str += ` ${colors[i]} ${cumulativeDegree}deg ${cumulativeDegree + chartData[i]}deg,`
}
cumulativeDegree += chartData[i]
}
//Set chart labels
setLabels(chartLabels)
set background value
setBackground(`conic-gradient(${str})`)
}
//Call the paint chart function if fruits exist
if(fruits) paintChart()
},[])
We will also write another effect hook that runs when the background variable changes to help us update the DOM element with the new background value.
React.useEffect(() => {
//Set the background of the DOM elements using the ref attribute
pieChartRef.current.style.background = background
}, [background])
Our Piechart is now all set, lastly, we have to add labels to make our data more intuitive. We will create a ChartLabel component and update our CSS accordingly. We will also add the ChartLabel component to our PieChart component accordingly
const ChartLabel = ({text, color}) => {
return (
<div className="chartLabel">
<div style={{background: color}} className="chartLabelIndicator"></div>
<div className="chartLabelText">{text}</div>
</div>
)
}
We will now update our Piechart component accordingly. The jsx being returned in our Piechart component now looks like this:
return (
<>
<div className="piechart" ref={pieChartRef}></div>
<div className="chartLabelContainer">
{
labels.map((label, index) => {
return (
<ChartLabel key={index} text={label} color = {colors[index]} />
)
})
}
</div>
</>
)
Putting everything together, we can see the final snippet of our code, and it looks this way
Ternary (Triangular) Charts
Ternary or triangular charts are best used for hierarchical data. There are situations where you want to display dynamic data in relation to their hierarchy, this particular pick will be the best for this situation.
Using our knowledge from creating a custom pie chart, we should begin to understand that there are two major things that we need to get our charts to work
A way to display multiple colors on the preferred chart
A way to make the displayed colors dynamic
We would start out by creating a multi-colored triangle using linear-graident
CSS function.
From here on out, it's all about creating our react component and adding dynamism like we did with the pie chart. We have to be careful to note that the linear-gradient
function does not make use of deg
as such, we will be switching to percentages and calculating our values based on 100%
The code for our ternary chart would look like this after we are finished:
const TernaryChart = ({fruits}) => {
const [background, setBackground] = React.useState('grey');
const [labels, setLabels] = React.useState([])
const colors = ['green', 'blue', 'indigo', 'red', 'orange', 'yellow', 'violet'];
const ternaryChartRef = React.useRef(null)
React.useEffect(() => {
const paintChart = () => {
let chartData = []
let chartLabels = []
for (let fruit in fruits){
chartData.push(fruits[fruit])
chartLabels.push(`${fruit}`)
}
const sum = chartData.reduce((x, y) => x + y)
chartData = chartData.map((x) => Math.round(100 * x/sum))
let str = ''
let cumulativeDegree = 0
for (let i = 0; i < chartData.length; i++) {
if(i === 0 && chartData.length > 1){
str += `${colors[i]} 0 ${chartData[i]}%,`
} else if(i + 1 === chartData.length) {
str += ` ${colors[i]} ${cumulativeDegree}% ${cumulativeDegree + chartData[i]}%`
} else {
str += ` ${colors[i]} ${cumulativeDegree}% ${cumulativeDegree + chartData[i]}%,`
}
cumulativeDegree += chartData[i]
}
setLabels(chartLabels)
setBackground(`linear-gradient(to bottom, ${str})`)
}
if(fruits) paintChart()
},[])
React.useEffect(() => {
ternaryChartRef.current.style.background = background
}, [background])
return (
<>
<div className="ternarychart" ref={ternaryChartRef}></div>
<div className="chartLabelContainer">
{
labels.map((label, index) => {
return (
<ChartLabel key={index} text={label} color = {colors[index]} />
)
})
}
</div>
</>
)
}
Putting everything together, here's a snippet of what our code should look like
Bonus
We can go ahead to create doughnut charts by cutting out a circular area beginning from the center, within our pie chart.
Summary
We have gone through the process of creating two custom charts using just react.js and CSS. From creating a pie chart using CSS, then making it dynamic using react and repeating the same with a triangular chart. This can come in handy in projects where we are doing minimal data visualization.
Cheers!