DEV Community

Cover image for Bar Stack & Line chart combined
Laetitia
Laetitia

Posted on

Bar Stack & Line chart combined

The visx library by airbnb is my to-go-to when building charts with React.

It’s well documented, based on d3, really flexible and customizable.

You can find a lot of articles explaining to you how to build a simple chart using visx, they even provide some sandbox.

When struggling with a chart, always go to the d3 documentation and do some research on how to perform this or that.

How can I align properly two charts of different types and scales?

I need to display some data over time, my x-axis is time-based in this example.

The stack bar chart needs by default a scale band.

I would like to add a line displaying other data based on the same x-axis values I’m using for the bar stack chart.

❌ Create two different x-axis scales

My first approach was to create two different x-axis scales. But because the line chart and the bar stack are not using the same scale type, the alignment of my two charts was off.

✅ Only use one scale type

So as a requirement, I could only use one scale type, since I don’t have much choice with the bar stack, it had to be a scale band.

import { scaleBand, scaleLinear, scaleOrdinal } from '@visx/scale';

... 

// create the scale that will be passed to the <BarStack /> component
const xScale = scaleBand({
  domain: selectedBarPoints.map(getX),
});
Enter fullscreen mode Exit fullscreen mode

In order to get the line chart aligned, I need to use my xScale somehow.

The key is to pass the xScale that we are using for the bar stack chart to the x props of the LinePath, but with a twist:

 x={d => xScale.bandwidth() / 2 + xScale(d.x)}
Enter fullscreen mode Exit fullscreen mode

Explanations

// width of the bar divided by 2 to get the middle
xScale.bandwidth() / 2

// the x position of the bar in the chart
xScale(d.x)
Enter fullscreen mode Exit fullscreen mode

Bar stack chart combined with a line chart

Here is a more "in-context" example, also adding dots on top of the line:

<svg ref={containerRef} width="100%" height={height}>
  <Group
    width={width}
    height={height}
    top={margin.top}
    left={margin.left}
  >
    <BarStack
      data={selectedBarPoints}
      x={getX}
      xScale={xScale}
      yScale={yScale}
    >
      {barStacks =>
        barStacks.map(barStack =>
          map(barStack.bars, (bar, index) => 
          (
              <rect
                x={bar.x}
                y={bar.y - 0.8}
                height={bar.height}
                width={bar.width}
              />
            )),
        )
      }
    </BarStack>
    {selectedLinePoints.map((datapoints, i) => (
        <Fragment key={`fragment-line-${datapoints.id}`}>
          <LinePath
            data={datapoints.data}
                        // here is where the magic happens
            x={d => xScale.bandwidth() / 2 + xScale(d.x)}
            y={d => yScale(d.y)}
          />
          {datapoints.data.map((dot) => (
            <g key={`line-glyph-${dot.x}`}>
              <GlyphCircle
                                // operate the same logic to position a dot on the line
                left={xScale.bandwidth() / 2 + xScale(dot.x)}
                top={yScale(dot.y)}
              />
            </g>
          ))}
        </Fragment>
      ))}
  </Group>
</svg>
Enter fullscreen mode Exit fullscreen mode

Happy coding charts!

Top comments (0)