Navigate back to the homepage
Browse Repo

Low-Level Charts in React

Catalin Pit
March 4th, 2021 · 3 min read

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 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.


A better option is to use a library based on D3, such as 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

The Setup Visx is split into multiple packages so to follow along you have the following options:

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]

1const data = [
2 [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.

1const xScale = scaleLinear({
2 domain: [1, 10],
3 range: [0 + padding, width - padding]
6const yScale = scaleLinear({
7 domain: [0, 50],
8 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.

1const colors = {
2 white: "#FFFFFF",
3 black: "#1B1B1B",
4 gray: "#98A7C0",
5 darkGray: "#2A2A2A",
6 accent: "#40FEAE",
7 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.

3 <svg height={height} width={width}>
4 <rect
5 x={0}
6 y={0}
7 width={width}
8 height={height}
9 style={{
10 fill:,
11 }}
12 rx={14}
13 />
14 </svg>

Moving forward, we’re going to add our axis, somewhere at the bottom of the SVG:

2<svg height={height} width={width}>
3 <rect.../>
4 <Axis
5 scale={xScale}
6 top={height - padding}
7 orientation="bottom"
8 stroke={colors.darkGray}
9 strokeWidth={1.5}
10 tickStroke={colors.darkGray}
11 tickLabelProps={() => ({
12 fill: colors.gray,
13 textAnchor: "middle",
14 verticalAnchor: "middle"
15 })}
16 />
18 <Axis
19 hideZero
20 scale={yScale}
21 numTicks={5}
22 left={padding}
23 orientation="left"
24 stroke={colors.darkGray}
25 strokeWidth={1.5}
26 tickStroke={colors.darkGray}
27 tickLabelProps={() => ({
28 fill: colors.gray,
29 textAnchor: "end",
30 verticalAnchor: "middle"
31 })}
32 tickFormat={(value) => `$${value}`}
33 />

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.


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:

2<svg height={height} width={width}>
3 <rect.../>
4 <Axis.../>
5 //Gradient & Marker (these need to be created once and used by ID ref.)
6 <LinearGradient
7 id="line-gradient"
8 from={colors.accent}
9 to={colors.darkAccent}
10 />
11 <MarkerCircle id="marker-circle" fill={colors.gray} size={1.5} refX={2} />
13 // Actual Line
14 <LinePath
15 data={data}
16 x={(d) => xScale(d[0])}
17 y={(d) => yScale(d[1])}
18 stroke="url('#line-gradient')"
19 strokeWidth={3}
20 curve={curveNatural}
21 markerEnd="url(#marker-circle)"
22 />

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.


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.

2 id="background-gradient"
3 from={colors.darkAccent}
4 to={}
8 data={data}
9 x={(d) => xScale(d[0])}
10 y={(d) => yScale(d[1])}
11 fill="url('#background-gradient')"
12 curve={curveNatural}


Last but not least, we’ll also add a label to our chart.

2 <Text
3 style={{
4 fill: colors.white,
5 fontSize: 24,
6 fontWeight: 600
7 }}
8 x={padding / 2}
9 y={padding}
10 >
11 Price evolution (over 10 days)
12 </Text>


Done! Our chart is ready. Feel free to extend it and play with other customizations. If you had troubles following along, here you can find a full demo. There are plenty other examples 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. OpenReplay is an open-source session replay stack for developers. It helps you replay 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.

OpenReplay Frontend Monitoring

OpenReplay 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.

More articles from OpenReplay Blog

Smart Optimization Techniques: Lazy Loading with React

Learn how to optimize the loading time of your React applications using Lazy Loading

March 4th, 2021 · 6 min read

How to forget about type errors in your React props with PropTypes

If you want to reduce debug time of your React apps drastically, then this post is for you.

February 25th, 2021 · 5 min read
© 2021 OpenReplay Blog
Link to $ to $ to $