Low-Level Charts in React
Tags: react, javascript, webdev
Published: 2021-03-04 11:41:18

_Author [Catalin Pit](https://blog.asayer.io/authors/catalin-pit)_

If you're a frontend developer and you start working on an analytics app, then data visualization will soon become a topic of high importance. So what do you usually do when your project requires something more than casually using chart libraries? That's when a low-level visualization library comes in handy. 

Maybe [D3](https://github.com/d3/d3) will pass through your mind, but don't forget that both React & D3  will want to manipulate the DOM, which is not the thing to wish for, as it can lead to weird behaviors and bugs.

![visx](https://i.imgur.com/Lzo6WX5.png)

A better option is to use a library based on D3, such as [Visx](https://github.com/airbnb/visx), from Airbnb. It was introduced to the public last year and it aims to solve three issues:

 - learnability (no one wants to spend ages learning how to make charts)
 - expressiveness (it needs to allow you to create almost anything you can imagine)
 - performance (must be optimized for speed)


## Getting started
Okay, enough talking. Let's see how we can use Visx to build a line chart which displays the price evolution of a product over a 10 days period. 

**The Result**
![chart-v1](https://i.imgur.com/5V4EGpy.png)

**The Setup**
Visx is split into [multiple packages](https://airbnb.io/visx/docs) so to follow along you have the following options:

 - start a boilerplate and install the whole library: `@visx/visx` / the chart primitives you need: `npm install @visx/axis @visx/shape ...`
 - [use my boilerplate on CodeSandbox (recommended)](#https://codesandbox.io/s/react-low-level-chart-starter-q8ykk?file=/src/App.js)

Before actually implementing it, we need to prepare our data and the scales it will be used on.

**The Data** - an array of paired values structured as `[day, price]`
```
const  data  =  [
	[1, 0],[2, 10],[3, 30],[4, 5],[5, 16],[6, 23],[7, 48],[8, 43],[9, 38],[10, 0]
];
```

Our data needs to be scaled to the chart's sizes, so for that we're going to set a domain and range for both axis. `scaleLinear` is imported from Visx and used for exactly this purpose.
```
const  xScale  =  scaleLinear({
	domain:  [1,  10],
	range:  [0  +  padding,  width  -  padding]
}); 

const  yScale  =  scaleLinear({
	domain:  [0,  50],
	range:  [height  -  padding,  padding  *  2]
});
```

 - `domain` represents the number of values available within an axis. 
 - `range` tells how big the axis will be, within our svg. Since we want our svg to also contain some padding, I've calculated the size of it based on our initial `height`, `width` & `padding` values.

One last part to take in consideration is adding some colors which will be used for styling.
```
const  colors  =  {
	white:  "#FFFFFF",
	black:  "#1B1B1B",
	gray:  "#98A7C0",
	darkGray:  "#2A2A2A",
	accent:  "#40FEAE",
	darkAccent:  "#256769"
};
```

Great. Let's move on and create the chart. For that, you already have a `svg` element as the wrapper of your component. Everything else will go inside of it. The first thing to do is to create is a rectangle, as big as the SVG.
```
...
return(
	<svg height={height} width={width}>
		<rect
			x={0}
			y={0}
			width={width}
			height={height}
			style={{
				fill:  colors.black,
			}}
			rx={14}
		/>
	</svg>
)
...
```
Moving forward, we're going to add our axis, somewhere at the bottom of the SVG:

```
...
<svg height={height} width={width}>
	<rect.../>
	<Axis
		scale={xScale}
		top={height - padding}
		orientation="bottom"
		stroke={colors.darkGray}
		strokeWidth={1.5}
		tickStroke={colors.darkGray}
		tickLabelProps={() => ({
		fill:  colors.gray,
		textAnchor:  "middle",
		verticalAnchor:  "middle"
		})}
	/>

	<Axis
		hideZero
		scale={yScale}
		numTicks={5}
		left={padding}
		orientation="left"
		stroke={colors.darkGray}
		strokeWidth={1.5}
		tickStroke={colors.darkGray}
		tickLabelProps={() => ({
			fill:  colors.gray,
			textAnchor:  "end",
			verticalAnchor:  "middle"
		})}
		tickFormat={(value) =>  `$${value}`}
	/>
</svg>
...
```
Those need to take in the `xScale` & `yScale` previously declared. Along with that, we also do some styling for the components. More about the available options can be found [here](https://airbnb.io/visx/docs/axis).

![chart-v2](https://i.imgur.com/v48ht2f.png)

Next, we're going to add the line of the chart, a marker for the end of it + a gradient for achieving a slick design:
```
...
<svg height={height} width={width}>
	<rect.../>
	<Axis.../>
	//Gradient & Marker (these need to be created once and used by ID ref.)
	<LinearGradient
		id="line-gradient"
		from={colors.accent}
		to={colors.darkAccent}
	/>
	<MarkerCircle id="marker-circle" fill={colors.gray} size={1.5} refX={2} />

	// Actual Line
	<LinePath
		data={data}
		x={(d) => xScale(d[0])}
		y={(d) => yScale(d[1])}
		stroke="url('#line-gradient')"
		strokeWidth={3}
		curve={curveNatural}
		markerEnd="url(#marker-circle)"
	/>
</svg>
```

In order to make the `LinePath` render, we need to pass in the original data, as well as map it to the scales we created. In our case, the X axis holds the days, so we want to use the first pair value from our `data` object and return it once mapped with xScale. Same happens for the Y axis where we map the second pair value from `data` and return an yScale value. To better understand it, you can `console.log` the values within the x and the y functions.

![chart-v3](https://i.imgur.com/xz23K8J.png)

Next, we can take it a step further and make the chart look more modern by adding a background shade to the line. To do that, include the following bit of code before the original `LinePath`.

```
<LinearGradient
	id="background-gradient"
	from={colors.darkAccent}
	to={colors.black}
/>

<LinePath
	data={data}
	x={(d) =>  xScale(d[0])}
	y={(d) =>  yScale(d[1])}
	fill="url('#background-gradient')"
	curve={curveNatural}
/>
```
![chart-v4](https://i.imgur.com/I0UTKrL.png)

Last but not least, we'll also add a label to our chart.
```
...
	<Text
		style={{
			fill:  colors.white,
			fontSize:  24,
			fontWeight:  600
		}}
		x={padding / 2}
		y={padding}
	>
		Price evolution (over 10 days)
	</Text>
</svg>
```
![chart-v1](https://i.imgur.com/5V4EGpy.png)

Done! Our chart is ready. Feel free to extend it and play with other customizations. If you had troubles following along, [here](https://codesandbox.io/s/react-low-level-chart-hrje5) you can find a full demo. There are [plenty other examples](https://airbnb.io/visx/gallery) on which you can get your hands on, so wait no more. Experiment!

## Observability for Production React Apps

Debugging React apps in production may be challenging and time consuming. Asayer is a frontend monitoring tool that replays everything your users do and shows how your app behaves and renders for every issue. It’s like having your browser’s inspector open while looking over your user’s shoulder.

![Asayer Frontend Monitoring](https://asayer-content.s3.eu-central-1.amazonaws.com/eb8f28fc4852447eb750d1747c53e692.png)

Asayer helps to quickly get to the root cause by reproducing issues as if they happened in your own browser. It also monitors your frontend performance by capturing key metrics such as page load time, memory consumption and slow network requests as well as *Redux* actions/state.

Happy debugging, for modern frontend teams - [Start monitoring your web app for free](https://asayer.io/register.html).