Categories
Visx

Create a React Horizontal Grouped Bar Chart with the Visx Library

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 horizontal grouped bar charts into our React app.

Install Required Packages

We have to install a few modules to create the grouped bar chart.

To get started, we run:

npm i @visx/axis @visx/group @visx/mock-data @visx/responsive @visx/scale @visx/shape

to install the packages.

Create the Chart

We can create the chart by adding the items provided by the modules.

We use the data from the @visx/mock-data module.

To create the chart, we write:

import React from "react";
import { BarGroupHorizontal, Bar } from "@visx/shape";
import { Group } from "@visx/group";
import { AxisLeft } from "@visx/axis";
import cityTemperature from "@visx/mock-data/lib/mocks/cityTemperature";
import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale";
import { timeParse, timeFormat } from "d3-time-format";

const blue = "#aeeef8";
const green = "#e5fd3d";
const purple = "#9caff6";
const background = "#612efb";
const defaultMargin = { top: 20, right: 20, bottom: 20, left: 50 };

const parseDate = timeParse("%Y-%m-%d");
const format = timeFormat("%b %d");
const formatDate = (date) => format(parseDate(date));
function max(arr, fn) {
  return Math.max(...arr.map(fn));
}

const data = cityTemperature.slice(0, 4);
const keys = Object.keys(data[0]).filter((d) => d !== "date");

const getDate = (d) => d.date;

const dateScale = scaleBand({
  domain: data.map(getDate),
  padding: 0.2
});
const cityScale = scaleBand({
  domain: keys,
  padding: 0.1
});
const tempScale = scaleLinear({
  domain: [0, max(data, (d) => max(keys, (key) => Number(d[key])))]
});
const colorScale = scaleOrdinal({
  domain: keys,
  range: [blue, green, purple]
});

function Example({ width, height, margin = defaultMargin, events = false }) {
  const xMax = width - margin.left - margin.right;
  const yMax = height - margin.top - margin.bottom;

dateScale.rangeRound([0, yMax]);
  cityScale.rangeRound([0, dateScale.bandwidth()]);
  tempScale.rangeRound([0, xMax]);

return width < 10 ? null : (
    <svg width={width} height={height}>
      <rect
        x={0}
        y={0}
        width={width}
        height={height}
        fill={background}
        rx={14}
      />
      <Group top={margin.top} left={margin.left}>
        <BarGroupHorizontal
          data={data}
          keys={keys}
          width={xMax}
          y0={getDate}
          y0Scale={dateScale}
          y1Scale={cityScale}
          xScale={tempScale}
          color={colorScale}
        >
          {(barGroups) =>
            barGroups.map((barGroup) => (
              <Group
                key={`bar-group-horizontal-${barGroup.index}-${barGroup.y0}`}
                top={barGroup.y0}
              >
                {barGroup.bars.map((bar) => (
                  <Bar
                    key={`${barGroup.index}-${bar.index}-${bar.key}`}
                    x={bar.x}
                    y={bar.y}
                    width={bar.width}
                    height={bar.height}
                    fill={bar.color}
                    rx={4}
                    onClick={() => {
                      if (events)
                        alert(
                          `${bar.key} (${bar.value}) - ${JSON.stringify(bar)}`
                        );
                    }}
                  />
                ))}
              </Group>
            ))
          }
        </BarGroupHorizontal>
        <AxisLeft
          scale={dateScale}
          stroke={green}
          tickStroke={green}
          tickFormat={formatDate}
          hideAxisLine
          tickLabelProps={() => ({
            fill: green,
            fontSize: 11,
            textAnchor: "end",
            dy: "0.33em"
          })}
        />
      </Group>
    </svg>
  );
}

export default function App() {
  return (
    <div className="App">
      <Example width="500" height="300" />
    </div>
  );
}

We set the colors of the bars with the blue , green and purple variables.

The background variable has the background color.

defaultMargin have the default margins.

parseDate and format have the date parsing and formatting functions.

We parse the date from the mock data so we can format them to display in the chart.

data has the data for the chart.

keys have the data for the x-axis.

dateScale have the date scale.

cityScale have the city data.

tempScale have the temperature values for the bars.

colorScale have the colors for the bars.

We computed the xMax and yMax values to get the max values for the x and y axes.

Then we call rangeRound to set the max values for the x and y-axis ranges.

Next, we return the svg element and add the chart parts inside to add the bar.

The Group component is the container for the bar parts.

BarGroupHorizontal lets us display the bar groups horizontally.

We set the color , width , and the scales for the bars with the y0 , y0Scale and y1Scale props.

xScale sets the x-axis scale which is the bar scale.

Then we map the barGroups to return the Bar s in the map callback.

We set the bar lengths with the width prop.

Finally, we add the AxisLeft component to render the y-axis.

Conclusion

We can use the modules provided by Visx to create a horizontal grouped bar chart in our React app.

Categories
Visx

Create a React Grouped Bar Chart with the Visx Library

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 grouped bar charts into our React app.

Install Required Packages

We have to install a few modules to create the grouped bar chart.

To get started, we run:

npm i @visx/axis @visx/group @visx/mock-data @visx/responsive @visx/scale @visx/shape

to install the packages.

Create the Chart

We can create the chart by adding the items provided by the modules.

We use the data from the @visx/mock-data module.

To create the chart, we write:

import React from "react";
import { Group } from "@visx/group";
import { BarGroup } from "@visx/shape";
import { AxisBottom } from "@visx/axis";
import cityTemperature from "@visx/mock-data/lib/mocks/cityTemperature";
import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale";
import { timeParse, timeFormat } from "d3-time-format";

const blue = "#aeeef8";
export const green = "#e5fd3d";
const purple = "#9caff6";
export const background = "#612efb";

const data = cityTemperature.slice(0, 8);
const keys = Object.keys(data[0]).filter((d) => d !== "date");
const defaultMargin = { top: 40, right: 0, bottom: 40, left: 0 };

const parseDate = timeParse("%Y-%m-%d");
const format = timeFormat("%b %d");
const formatDate = (date) => format(parseDate(date));

const getDate = (d) => d.date;

const dateScale = scaleBand({
  domain: data.map(getDate),
  padding: 0.2
});
const cityScale = scaleBand({
  domain: keys,
  padding: 0.1
});
const tempScale = scaleLinear({
  domain: [
    0,
    Math.max(...data.map((d) => Math.max(...keys.map((key) => Number(d[key])))))
  ]
});
const colorScale = scaleOrdinal({
  domain: keys,
  range: [blue, green, purple]
});

function Example({ width, height, events = false, margin = defaultMargin }) {
  const xMax = width - margin.left - margin.right;
  const yMax = height - margin.top - margin.bottom;
  dateScale.rangeRound([0, xMax]);
  cityScale.rangeRound([0, dateScale.bandwidth()]);
  tempScale.range([yMax, 0]);

  return width < 10 ? null : (
    <svg width={width} height={height}>
      <rect
        x={0}
        y={0}
        width={width}
        height={height}
        fill={background}
        rx={14}
      />
      <Group top={margin.top} left={margin.left}>
        <BarGroup
          data={data}
          keys={keys}
          height={yMax}
          x0={getDate}
          x0Scale={dateScale}
          x1Scale={cityScale}
          yScale={tempScale}
          color={colorScale}
        >
          {(barGroups) =>
            barGroups.map((barGroup) => (
              <Group
                key={`bar-group-${barGroup.index}-${barGroup.x0}`}
                left={barGroup.x0}
              >
                {barGroup.bars.map((bar) => (
                  <rect
                    key={`bar-group-bar-${barGroup.index}-${bar.index}-${bar.value}-${bar.key}`}
                    x={bar.x}
                    y={bar.y}
                    width={bar.width}
                    height={bar.height}
                    fill={bar.color}
                    rx={4}
                    onClick={() => {
                      if (!events) return;
                      const { key, value } = bar;
                      alert(JSON.stringify({ key, value }));
                    }}
                  />
                ))}
              </Group>
            ))
          }
        </BarGroup>
      </Group>
      <AxisBottom
        top={yMax + margin.top}
        tickFormat={formatDate}
        scale={dateScale}
        stroke={green}
        tickStroke={green}
        hideAxisLine
        tickLabelProps={() => ({
          fill: green,
          fontSize: 11,
          textAnchor: "middle"
        })}
      />
    </svg>
  );
}

export default function App() {
  return (
    <div className="App">
      <Example width="500" height="300" />
    </div>
  );
}

We set the color of the bars with the blue , green , and purple variables.

background is the background color.

The data variable has the mock data values we want to get and display in the bars.

keys have the x-axis values.

defaultMargin has the margin styles.

We create the parseDate and format functions from D3’s time parsing and formatting functions.

Then we create the day and city scales with the dateScale and cityScale variables.

The cityScale values are the bars.

tempScale are the bar heights.

colorScale have the color of the bars.

The Example component has the bar chart. We put everything together in there.

The xMax and yMax values are the max x and y-axis values respectively.

We use it to set the max values of the dateScale and tempScale.

In the return statement, we put everything in the svg element.

Group has the chart components.

We map the barGroups into rect elements to display the bars.

We set the width and height prop to set their width and height.

The x-axis is rendered by the AxisBottom component.

We set the top prop to set the location of the axis.

tickFormat has the ticks.

tickLabelProps have the label styles.

Conclusion

We can add grouped bar charts easily into our React app with the Visx library.

Categories
Visx

Create a React Area Difference Chart with the Visx Library

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 area difference charts into our React app.

Install Required Libraries

We’ve to install multiple modules provided by the Visx library to create the Area Difference Chart.

To do this, we run:

npm i @visx/axis @visx/curve @visx/grid @visx/group @visx/mock-data @visx/responsive @visx/scale @visx/shape @visx/threshold

Create the Chart

Once we installed all the required libraries, we add the chart by writing:

import React from "react";
import { Group } from "@visx/group";
import { curveBasis } from "@visx/curve";
import { LinePath } from "@visx/shape";
import { Threshold } from "@visx/threshold";
import { scaleTime, scaleLinear } from "@visx/scale";
import { AxisLeft, AxisBottom } from "@visx/axis";
import { GridRows, GridColumns } from "@visx/grid";
import cityTemperature from "@visx/mock-data/lib/mocks/cityTemperature";

export const background = "#f3f3f3";

const date = (d) => new Date(d.date).valueOf();
const ny = (d) => Number(d["New York"]);
const sf = (d) => Number(d["San Francisco"]);

const timeScale = scaleTime({
  domain: [
    Math.min(...cityTemperature.map(date)),
    Math.max(...cityTemperature.map(date))
  ]
});
const temperatureScale = scaleLinear({
  domain: [
    Math.min(...cityTemperature.map((d) => Math.min(ny(d), sf(d)))),
    Math.max(...cityTemperature.map((d) => Math.max(ny(d), sf(d))))
  ],
  nice: true
});

const defaultMargin = { top: 40, right: 30, bottom: 50, left: 40 };

function Theshold({ width, height, margin = defaultMargin }) {
  if (width < 10) return null;

const xMax = width - margin.left - margin.right;
  const yMax = height - margin.top - margin.bottom;

timeScale.range([0, xMax]);
  temperatureScale.range([yMax, 0]);

return (
    <div>
      <svg width={width} height={height}>
        <rect
          x={0}
          y={0}
          width={width}
          height={height}
          fill={background}
          rx={14}
        />
        <Group left={margin.left} top={margin.top}>
          <GridRows
            scale={temperatureScale}
            width={xMax}
            height={yMax}
            stroke="#e0e0e0"
          />
          <GridColumns
            scale={timeScale}
            width={xMax}
            height={yMax}
            stroke="#e0e0e0"
          />
          <line x1={xMax} x2={xMax} y1={0} y2={yMax} stroke="#e0e0e0" />
          <AxisBottom
            top={yMax}
            scale={timeScale}
            numTicks={width > 520 ? 10 : 5}
          />
          <AxisLeft scale={temperatureScale} />
          <text x="-70" y="15" transform="rotate(-90)" fontSize={10}>
            Temperature (°F)
          </text>
          <Threshold
            id={`${Math.random()}`}
            data={cityTemperature}
            x={(d) => timeScale(date(d)) ?? 0}
            y0={(d) => temperatureScale(ny(d)) ?? 0}
            y1={(d) => temperatureScale(sf(d)) ?? 0}
            clipAboveTo={0}
            clipBelowTo={yMax}
            curve={curveBasis}
            belowAreaProps={{
              fill: "violet",
              fillOpacity: 0.4
            }}
            aboveAreaProps={{
              fill: "green",
              fillOpacity: 0.4
            }}
          />
          <LinePath
            data={cityTemperature}
            curve={curveBasis}
            x={(d) => timeScale(date(d)) ?? 0}
            y={(d) => temperatureScale(sf(d)) ?? 0}
            stroke="#222"
            strokeWidth={1.5}
            strokeOpacity={0.8}
            strokeDasharray="1,2"
          />
          <LinePath
            data={cityTemperature}
            curve={curveBasis}
            x={(d) => timeScale(date(d)) ?? 0}
            y={(d) => temperatureScale(ny(d)) ?? 0}
            stroke="#222"
            strokeWidth={1.5}
          />
        </Group>
      </svg>
    </div>
  );
}

export default function App() {
  return (
    <div className="App">
      <Theshold width="500" height="300" />
    </div>
  );
}

We use the mock data provided by the Visx library to create the chart.

We create the timeScale scale for the x-axis.

And we create the temperatureScale for the y-axis.

We call scaleTime and scaleLinear respectively to scale the data to display the chart.

Next, we set the margins with the defaultMargin object.

Then we create the Threshold component which has the area difference chart.

In it, we computed the xMax and yMax values to create the max value for the x and y axes respectively.

Then we call the range method to create the time and temperature scales for the chart.

Next, we add the svg element to add the container for the chart.

rect has the rectangle that surrounds the chart.

Group has items we need to form the chart.

GridRows has the grid rows.

We set the scale prop to the temperatureScale to display the temperatures.

Likewise, we do the same for the GridColumns component but scale is set to timeScale .

line has the perimeter lines for the chart.

AxisBottom has the x-axis.

AxisLeft is the y-axis.

We add the Threshold component to bad the fill between the lines.

And we add the lines with the LinePath component.

We set the data by setting the data prop.

The x and y props return the x and y values.

Finally, in App , we render the Threshold component we created and set the width and height to display the chart.

Conclusion

We can create area difference charts easily in our React app with the Visx library.

Categories
Visx

Create a React Filled Line Graph with the Visx Library

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.

Categories
Visx

Create a React Bar Graph with the Visx Library

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 bar 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/mock-data @visx/group @visx/shape @visx/scale

The @visx/mock-data library has mock data we can use in our bar graphs.

Create the Bar Graph

To create the bar graph, we add the following code:

import React from "react";
import { letterFrequency } from "@visx/mock-data";
import { Group } from "@visx/group";
import { Bar } from "@visx/shape";
import { scaleLinear, scaleBand } from "@visx/scale";

const data = letterFrequency;
const width = 500;
const height = 300;
const margin = { top: 20, bottom: 20, left: 20, right: 20 };
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
const x = (d) => d.letter;
const y = (d) => +d.frequency * 100;

const xScale = scaleBand({
  range: [0, xMax],
  round: true,
  domain: data.map(x),
  padding: 0.4
});
const yScale = scaleLinear({
  range: [yMax, 0],
  round: true,
  domain: [0, Math.max(...data.map(y))]
});

const compose = (scale, accessor) => (data) => scale(accessor(data));
const xPoint = compose(xScale, x);
const yPoint = compose(yScale, y);

function BarGraph(props) {
  return (
    <svg width={width} height={height}>
      {data.map((d, i) => {
        const barHeight = yMax - yPoint(d);
        return (
          <Group key={`bar-${i}`}>
            <Bar
              x={xPoint(d)}
              y={yMax - barHeight}
              height={barHeight}
              width={xScale.bandwidth()}
              fill="#fc2e1c"
            />
          </Group>
        );
      })}
    </svg>
  );
}

export default function App() {
  return (
    <div>
      <BarGraph />
    </div>
  );
}

data has the data for our graph.

width and height are the width and height of the graph.

margin has the margins.

xMax has the max x-axis value.

yMax has the max y-axis value.

x is a function to return the data to display for the x-axis.

And y is the function to return the data to display on the y-axis.

xScale lets us create the x-axis values to add to the graph.

yScale has the y-axis values for the graph.

Then we create the compose function to scale the x and y axis values to fit in the graph.

Once we did that, we create the BarGraph component with the Group and Bar components.

Bar has the bars.

Group has the container for the bars.

We set the fill color for the bar.

And the x and y values

y is set to the height of the bar.

The width is scaled according to the screen size.

Conclusion

We can add bar graphs easily into our React app with the Visx library.