Visx is a library that lets us add graphics to our React app easily.
In this article, we’ll look at how to use it to add filled line graphs into our React app.
Getting Started
To get started, we’ve to install several modules provided by Visx.,
To install the ones needed by the bar graph, we run:
$ npm install --save @visx/axis @visx/curve @visx/gradient @visx/grid @visx/group @visx/mock-data @visx/react-spring @visx/responsive @visx/shape @visx/scale
The @visx/mock-data
library has mock data we can use in our bar graphs.
Add the Filled Line Graph
To add the filled line graph, we write:
import React, { useState, useMemo } from "react";
import AreaClosed from "@visx/shape/lib/shapes/AreaClosed";
import { curveMonotoneX } from "@visx/curve";
import {
scaleUtc,
scaleLinear,
scaleLog,
scaleBand,
coerceNumber
} from "@visx/scale";
import { Orientation } from "@visx/axis";
import {
AnimatedAxis,
AnimatedGridRows,
AnimatedGridColumns
} from "@visx/react-spring";
import { LinearGradient } from "@visx/gradient";
import { timeFormat } from "d3-time-format";
export const backgroundColor = "#da7cff";
const axisColor = "#fff";
const tickLabelColor = "#fff";
export const labelColor = "#340098";
const gridColor = "#6e0fca";
const margin = {
top: 40,
right: 150,
bottom: 20,
left: 50
};
const tickLabelProps = () => ({
fill: tickLabelColor,
fontSize: 12,
fontFamily: "sans-serif",
textAnchor: "middle"
});
const getMinMax = (vals) => {
const numericVals = vals.map(coerceNumber);
return [Math.min(...numericVals), Math.max(...numericVals)];
};
function Example({
width: outerWidth = 800,
height: outerHeight = 800,
showControls = true
}) {
const width = outerWidth - margin.left - margin.right;
const height = outerHeight - margin.top - margin.bottom;
const [dataToggle] = useState(true);
const [animationTrajectory] = useState("center");
const axes = useMemo(() => {
const linearValues = dataToggle ? [0, 2, 4, 6, 8, 10] : [6, 8, 10, 12];
return [
{
scale: scaleLinear({
domain: getMinMax(linearValues),
range: [0, width]
}),
values: linearValues,
tickFormat: (v, index, ticks) =>
index === 0
? "first"
: index === ticks[ticks.length - 1].index
? "last"
: `${v}`,
label: "linear"
}
];
}, [dataToggle, width]);
if (width < 10) return null;
const scalePadding = 40;
const scaleHeight = height / axes.length - scalePadding;
const yScale = scaleLinear({
domain: [100, 0],
range: [scaleHeight, 0]
});
return (
<>
<svg width={outerWidth} height={outerHeight}>
<LinearGradient
id="visx-axis-gradient"
from={backgroundColor}
to={backgroundColor}
toOpacity={0.5}
/>
<rect
x={0}
y={0}
width={outerWidth}
height={outerHeight}
fill={"url(#visx-axis-gradient)"}
rx={14}
/>
<g transform={`translate(${margin.left},${margin.top})`}>
{axes.map(({ scale, values, label, tickFormat }, i) => (
<g
key={`scale-${i}`}
transform={`translate(0, ${i * (scaleHeight + scalePadding)})`}
>
<AnimatedGridRows
key={`gridrows-${animationTrajectory}`}
scale={yScale}
stroke={gridColor}
width={width}
numTicks={dataToggle ? 1 : 3}
animationTrajectory={animationTrajectory}
/>
<AnimatedGridColumns
key={`gridcolumns-${animationTrajectory}`}
scale={scale}
stroke={gridColor}
height={scaleHeight}
numTicks={dataToggle ? 5 : 2}
animationTrajectory={animationTrajectory}
/>
<AreaClosed
data={values.map((x) => [
(scale(x) ?? 0) +
("bandwidth" in scale &&
typeof scale.bandwidth !== "undefined"
? scale.bandwidth() / 2
: 0),
yScale(10 + Math.random() * 90)
])}
yScale={yScale}
curve={curveMonotoneX}
fill={gridColor}
fillOpacity={0.2}
/>
<AnimatedAxis
key={`axis-${animationTrajectory}`}
orientation={Orientation.bottom}
top={scaleHeight}
scale={scale}
tickFormat={tickFormat}
stroke={axisColor}
tickStroke={axisColor}
tickLabelProps={tickLabelProps}
tickValues={
label === "log" || label === "time" ? undefined : values
}
numTicks={label === "time" ? 6 : undefined}
label={label}
labelProps={{
x: width + 30,
y: -10,
fill: labelColor,
fontSize: 18,
strokeWidth: 0,
stroke: "#fff",
paintOrder: "stroke",
fontFamily: "sans-serif",
textAnchor: "start"
}}
animationTrajectory={animationTrajectory}
/>
</g>
))}
</g>
</svg>
</>
);
}
export default function App() {
return (
<div>
<Example width="500" height="300" />
</div>
);
}
We add the backgroundColor
to set the background color.
axisColor
has the axis color.
tickLabelColor
has the x-axis tick label’s color.
labelColor
has the y-axis label color.
gridColor
has the grid color.
We change the text styles with the tickLabelProps
function.
To add the graph content, we add the axes
array.
We compute the values for the graph from the dataToggle
object.
linearValues
has the linear scale value.
We have the yScale
object to add the y-axis values.
In the JSX we return, we add the SVG, rect
rectangle element, and then in the axes.map
callback, we return the graph.
The graph has the g
element with the SVG group.
AnimatedGridRows
has the animated grid rows.
We pass in the yScale
value to it to display the y values.
stroke
sets the stroke.
width
sets the width.
AnimatedGridColumns
has the grid columns.
numTicks
lets us set the number of ticks on the x-axis.
We have similar code with the AnimatedGridColumns
component to add the columns.
AreaClosed
has the filled line graph.
AnimatedAxis
has the animated x-axis.
We set the label styles with the labelProps
.
tickValues
has the tick values.
Conclusion
We can add a filled line graph into our React app with Visx.