Cube.js Blog

React Visualization Libraries in 2019

Author avatarArtyom Keydunov/Visualizations/April 18, 2019
React Visualization Libraries in 2019
Show Original

While working on Cube.js, we’re seeing a lot of different visualization libraries. Given that Cube.js provides an API layer for analytics on top of an SQL database and doesn’t play on the visualization field, any charting library can be used to build dashboards with it. That’s why we’re always on a search for a nice and developer-friendly visualization library.

The list below is for React-specific libraries. I’ll try to build almost the same stacked bar chart with axes formatting, legend and tooltip using every one of it. For the data backend, we’ll use Cube.js hosted on Heroku. I’ll also use Cube.js React binding, which uses the render props technique and doesn’t render anything itself, letting us build whatever we want with any charting library.

Along the way, I’ll also use two very handy libraries—moment.js and numeral.js for dates and numbers formatting, respectively.

As a data input, we are going to use the resultSet.chartPivot() method from the Cube.js Javascript client. It returns an array of data, and in our case, it looks like this:

[
  {
    "x": "2017-01-01T00:00:00.000",
    "completed, Orders.count": 208,
    "processing, Orders.count": 252,
    "shipped, Orders.count": 233
  },
  {
    "x": "2017-02-01T00:00:00.000",
    "completed, Orders.count": 188,
    "processing, Orders.count": 209,
    "shipped, Orders.count": 222
  },]

Now, let’s jump to the list.

If you don’t see your favorite library or just want me to add one more—just ping me in this Public Slack Community. I’m happy to add as many libraries as I can to the list.

Recharts

Recharts provides a set of modular charting components and does a great job by letting you mix those components together to easily build things like a composed line and bar chart.

It is the most popular library to date. It has more than 11k stars on Github, but a huge number (600 to date) of open issues as well.

The documentation is quite extensive but lacks details in some parts. You can find a bunch of examples at Rechart’s website, which could be a good starting point to building your own charts.

Recharts has a bunch of options for customization. Besides that, it allows low-level customization via custom SVG elements.

Here is how to build our stacked bar chart in Recharts:

In this and all the following examples I'm using colors, dateFormatter, and numberFormatter variables. Here is how they are defined:

const numberFormatter = item => numeral(item).format("0,0");
const dateFormatter = item => moment(item).format("MMM YY");
const colors = ["#7DB3FF", "#49457B", "#FF7C78"];
export default ({ resultSet, colors, dateFormatter, numberFormatter }) => (
  <ResponsiveContainer width="100%" height={300}>
    <BarChart data={resultSet.chartPivot()}>
      <XAxis tickFormatter={dateFormatter} dataKey="x" />
      <YAxis tickFormatter={numberFormatter} />
      {resultSet.seriesNames().map((series, i) => (
        <Bar
          stackId="a"
          dataKey={series}
          name={series.split(",")[0]}
          fill={colors[i]}
        />
      ))}
      <Legend />
      <Tooltip labelFormatter={dateFormatter} formatter={numberFormatter} />
    </BarChart>
  </ResponsiveContainer>
);

Victory

Victory follows the same composable pattern as Recharts. It is developed by Formidable, which has other solid open-source libraries besides Victory as well. It is the only library on the list that also works with React Native.

It’s the second most popular library after Recharts with more than 7K stars on Github, but has 5 times less open issues than Recharts. Victory has a community chat on Spectrum.

It is well documented and has an examples gallery.

Victory comes with two themes: grayscale and material, and allows you to create customer themes to have a consistent look across all the charts.

Below you can see the code for our stacked bar chart with Victory:

const transformResult = (series, resultSet) =>
  resultSet.chartPivot().map(element => ({ x: element.x, y: element[series] }));

export default ({ resultSet, dateFormatter, colors, numberFormatter }) => (
  <div height={300}>
    <VictoryChart
      containerComponent={
        <VictoryVoronoiContainer
          voronoiDimension="x"
          labels={(d, i) => `${resultSet.seriesNames()[i]}: ${d.y}`}
          labelComponent={
            <VictoryTooltip cornerRadius={0} flyoutStyle={{ fill: "white" }} />
          }
        />
      }
      domainPadding={{ x: 20, y: [0, 20] }}
    >
      <VictoryLegend
        colorScale={colors}
        data={resultSet.seriesNames().map(series => ({
          name: series.split(",")[0]
        }))}
        orientation="horizontal"
        y={275}
        x={130}
      />
      <VictoryAxis tickFormat={dateFormatter} tickCount={8} />
      <VictoryAxis dependentAxis />
      <VictoryStack colorScale={colors}>
        {resultSet.seriesNames().map((series, i) => (
          <VictoryBar key={i} data={transformResult(series, resultSet)} />
        ))}
      </VictoryStack>
    </VictoryChart>
  </div>
);

Nivo

As well as Recharts and Victory, Nivo is built on top of D3 and is React-specific. But unlike previous libraries it is not composable. It provides one component per chart type and this component is configured via multiple props. Nivo is distributed as a set of packages for specific chart types, for example, @nivo/bar. So, one needs to install a specific package to use a specific chart type.

The project itself is quite active; it has more than 5k stars on Github and almost 300 members in the community chat.

It has interactive documentation, where you can build config for your chart. Although it is nice, It lacks a good old text API reference and search option. It also has a Storybook with examples. It helped me a lot to shortcut the time to build the first chart. Same as Victory, Nivo lets you create your own theme to have a consistent look across all the charts.

We’re going to use the @nivo/bar package for our stack bar chart; you can find a code sample and CodeSandbox demo below.

//https://github.com/plouc/nivo/issues/138#issuecomment-373015114
const ticksFormmater = (ticksCount, value, data, dateFormatter) => {
  const valueIndex = data.map(i => i.x).indexOf(value);
  if (valueIndex % Math.floor(data.length / ticksCount) === 0) {
    return dateFormatter(value);
  }

  return "";
};

export default ({ resultSet, colors, dateFormatter, numberFormatter }) => (
  <div style={{ height: 300 }}>
    <ResponsiveBar
      enableLabel={false}
      colors={colors}
      data={resultSet.chartPivot()}
      keys={resultSet.seriesNames()}
      indexBy="x"
      enableGridY={false}
      padding={0.3}
      margin={{ top: 60, right: 80, bottom: 60, left: 40 }}
      axisLeft={{
        format: numberFormatter
      }}
      axisBottom={{
        format: value =>
          ticksFormmater(8, value, resultSet.chartPivot(), dateFormatter)
      }}
      tooltip={({ id, value, color }) => (
        <strong style={{ color }}>
          {id.split(",")[0]}: {numberFormatter(value)}
        </strong>
      )}
      legends={[
        {
          anchor: "bottom",
          direction: "row",
          translateY: 50,
          itemsSpacing: 2,
          itemWidth: 150,
          itemHeight: 20,
          itemDirection: "left-to-right"
        }
      ]}
    />
  </div>
);

BizCharts

BizCharts is React-specific visualization library based on G2, a visualization grammar. It is backed by Alibaba and plays nicely with Ant Design Framework.

It has almost 4k stars on Github, but the majority of development is in Chinese, same for most of the documentation. I think the team is doing a great job of translating the docs, but it is still a work in progress.

Though, it lacks documentation in English the API is pretty straightforward. The only hard thing is to re-format the data into the specific format for the stacked chart.

const stackedChartData = resultSet => {
  const data = resultSet
    .pivot()
    .map(({ xValues, yValuesArray }) =>
      yValuesArray.map(([yValues, m]) => ({
        x: resultSet.axisValuesString(xValues, ", "),
        color: resultSet.axisValuesString(yValues, ", "),
        measure: m && Number.parseFloat(m)
      }))
    )
    .reduce((a, b) => a.concat(b));

  return data;
};

export default ({ resultSet, dateFormatter, colors, numberFormatter }) => (
      <Chart
        scale={{ x: { tickCount: 10 } }}
        height={400}
        data={stackedChartData(resultSet)}
        forceFit
      >
        <Axis name="x" label={{ formatter: dateFormatter }} />
        <Axis label={{ formatter: numberFormatter }} name="measure" />
        <Tooltip />
        <Geom
          type="intervalStack"
          position={`x*measure`}
          color={["color", colors]}
        />
        <Legend itemFormatter={item => item.split(",")[0]} />
      </Chart>
    )}
  />
);

React-vis

React-vis is being developed by Uber and seems quite active with 5.4k Github stars, 150 open issues, and a lot of recent commits. It is modular, like most of the libraries on the list. It comes with some nice default styles, which should be imported separately as CSS files.

Same as Nivo, it has Storybook with examples. The components API is also well documented. On the customization side you can control the look and feel via custom CSS styles. React-vis also provides a specific component for building custom SVG charts—CustomSVGSeries.

The API is similar to Victory's. You can see I'm using almost the same transformResult from Victory's snippet.

const transformResult = (series, resultSet, dateFormatter) =>
  resultSet
    .chartPivot()
    .map(element => ({ x: Date.parse(element.x), y: element[series] }));

export default ({ resultSet, dateFormatter, colors, numberFormatter }) => (
  <XYPlot xType="time" height={300} width={500} stackBy="y">
    <XAxis tickFormat={dateFormatter} tickSize={8} />
    <YAxis />
    {resultSet.seriesNames().map((series, i) => (
      <VerticalBarSeries
        cluster="stack 1"
        key={i}
        color={colors[i]}
        data={transformResult(series, resultSet, dateFormatter)}
      />
    ))}
    <DiscreteColorLegend
      colors={colors}
      items={resultSet.seriesNames().map(i => i.split(",")[0])}
      orientation="horizontal"
      style={{ position: "absolute", left: 130, bottom: -30 }}
    />
  </XYPlot>
);

If you don’t see your favorite library or just want me to add one more—just ping me in this Public Slack Community. I’m happy to add as many libraries as I can to the list.

You may also like:

by Artyom Keydunov on April 18, 2019