React/ReactJS: Multi-Color Progress Bar

In this tutorial, we will learn how to create a multi-color progress bar as a ReactJS component.

Creating the Component

We first create a kind of template for the component which we will call MultiColorProgressBar. It consists of the wrapper <div className="multicolor-bar"> which contains four child <div> elements. The values (in percentages) will go inside <div className="values">, the vertical graduations inside <div className="scale">, the coloured bars inside <div className="bars"> and the legends inside <div className="legends">.

					
						class MultiColorProgressBar extends Component {
						  constructor(props) {
						    super(props);    
						  }
						  render() {
						    return (
						      <div className="multicolor-bar">
						      	<div className="values">
						      	</div>
						      	<div className="scale">
						      	</div>
						      	<div className="bars">
						      	</div>
						      	<div className="legends">
						      	</div>
						      </div>
						    );
						  }
						}
						export default MultiColorProgressBar;
					
				

This component will be passed the following data (props),

					
					let readings = [
						{
							name: 'Apples',
							value: 60,
							color: '#eb4d4b'
						},
						{
							name: 'Blueberries',
							value: 7,
							color: '#22a6b3'
						},
						{
							name: 'Guavas',
							value: 23,
							color: '#6ab04c'
						},
						{
							name: 'Grapes',
							value: 10,
							color: '#e056fd'
						}
					]
					
				

with which we will be forming our multi-color progress bar. The passed data, which is within this.props, is assigned to the const named parent inside the render() function. Below, we show how to generate an array of divs using the map() function, which will display the percentages on the top row.

					
						render() {
						  	const parent = this.props;		 
						  	let values = parent.readings && parent.readings.length && parent.readings.map(function(item, i) {
						  		if(item.value > 0) {
							  		return (
							  			<div className="value" style={{'color': item.color, 'width': item.value + '%'}}  key={i}>
							  				<span>{item.value}%</span>
							  			</div>
							  		)
						  		}
						  	}, this);
						 }
					
				

These generated divs are assigned to the variable values which will be rendered inside the <div className="values">, enclosed as the expression {values}.

					
					return (
					    <div className="multicolor-bar">
					    	<div className="values">
					    		{values}
					    	</div>
					    </div>
					);
					
				

To avoid displaying 0 while fetching values asynchronously, we modify our expression above to display nothing till the values are available.

					
					return (
					    <div className="multicolor-bar">
					    	<div className="values">
					    		{values == ''?'':values}
					    	</div>
					    </div>
					);
					
				

We do the same for the scale, the colored bars and the legends as shown below —

					
						class MultiColorProgressBar extends Component {
						  constructor(props) {
						    super(props);    
						  }
						  render() {
						  	const parent = this.props;
						  	let values = parent.readings && parent.readings.length && parent.readings.map(function(item, i) {
						  		if(item.value > 0) {
							  		return (
							  			<div className="value" style={{'color': item.color, 'width': item.value + '%'}}  key={i}>
							  				<span>{item.value}%</span>
							  			</div>
							  		)
						  		}
						  	}, this);
						  	let calibrations = parent.readings && parent.readings.length && parent.readings.map(function(item, i) {
						  		if(item.value > 0) {
							  		return (
							  			<div className="graduation" style={{'color': item.color, 'width': item.value + '%'}}  key={i}>
							  				<span>|</span>
							  			</div>
							  		)
						  		}
						  	}, this);
						  	let bars = parent.readings && parent.readings.length && parent.readings.map(function(item, i) {
						  		if(item.value > 0) {
							  		return (
							  			<div className="bar" style={{'backgroundColor': item.color, 'width': item.value + '%'}}  key={i}>
							  			</div>
							  		)
						  		}
						  	}, this);
						  	let legends = parent.readings && parent.readings.length && parent.readings.map(function(item, i) {
						  	  	if(item.value > 0) {
							      	return ( 
							      		<div className="legend" key={i}>
							      			<span className="dot" style={{'color': item.color}}>●</span>
							      			<span className="label">{item.name}</span>
							      		</div>
							       )
						       }
						    }, this);
						    return (
						      <div className="multicolor-bar">
						      	<div className="values">
						      		{values == ''?'':values}
						      	</div>
						      	<div className="scale">
						      		{calibrations == ''?'':calibrations}
						      	</div>
						      	<div className="bars">
						      		{bars == ''?'':bars}
						      	</div>
						      	<div className="legends">
						      		{legends == ''?'':legends}
						      	</div>
						      </div>
						    );
						  }
						}
						export default MultiColorProgressBar;
					
				

The style for our MultiColorProgressBar component is as below:

					
						.multicolor-bar {
							margin: 20px 20%;
						}
						.multicolor-bar .values .value {
							float: left;
							text-align: center;
						}
						.multicolor-bar .scale .graduation {
							float: left;
							text-align: center;
						}
						.multicolor-bar .bars .bar {
							float: left;
							height: 10px;
						}
						.multicolor-bar .bars div.bar:first-of-type {
						    border-top-left-radius: 5px;
						    border-bottom-left-radius: 5px;
						}
						.multicolor-bar .bars div.bar:last-of-type {
						    border-top-right-radius: 5px;
						    border-bottom-right-radius: 5px;
						}
						.multicolor-bar .legends {
							text-align: center;
						}
						.multicolor-bar .legends .legend {
						    display: inline-block;
						    margin: 0 5px;
						    text-align: center;
						}
						.multicolor-bar .legends .legend .dot {
							font-size: 25px;
							vertical-align: middle;
						}
						.multicolor-bar .legends .legend .label {
							margin-left: 2px;
							vertical-align: middle;
						}
					
				

We import and load our component inside index.js, passing the array this.readings consisting of data as an argument (props).

					
						this.readings = [
							{
								name: 'Apples',
								value: 60,
								color: '#eb4d4b'
							},
							{
								name: 'Blueberries',
								value: 7,
								color: '#22a6b3'
							},
							{
								name: 'Guavas',
								value: 23,
								color: '#6ab04c'
							},
							{
								name: 'Grapes',
								value: 10,
								color: '#e056fd'
							}
						];
						ReactDOM.render(<MultiColorProgressBar readings={this.readings}/>, 
							document.getElementById('root'));
					
				

The full code is available on CodePen also.

See the Pen Multi-Color Progress Bar in ReactJS by Dennis Gabil (@dennisgabil) on CodePen.

We finally get our progress bar.