Stacked Area charts

Dataviz logo representing a StackedArea chart.

A stacked area chart is an evolution of an area chart used to display the evolution of several groups in a dataset. This section explains how to build it with d3.js and react. It focus on stacking, so make sure to read the area chart section first.

Useful links

The Data

Most of the time the input dataset is an array where each item is an object.
Each object provides information for a step on the X axis. It has a value like x that provides the exact position on the X axis. It then has several numeric values, one for each group of the dataset.


Here is a minimal example:

const data = [
  {
    x: 1,
    groupA: 38,
    groupB: 19,
  },
  {
    x: 2,
    groupA: 16,
    groupB: 14,
  },
  ...
];

→ Wide and Long formats

The format described above is often called the wide format. Another common format is the long format, where each object in the array provides information for 1 group only. (The array becomes way longer 🙃)

If your dataset is formatted using the long format, you can transform it using the pivotWider function below:

Pivot function
type LongDataItem = {
  date: string;
  group: string;
  value: number;
};

type WideDataItem = {
  date: string;
} & { [key: string]: number }

const pivotWider = (data: LongDataItem[]) => {
  const result: WideDataItem[] = [];

  data.forEach((item) => {
      const existingEntry = result.find((entry) => entry.date === item.date);

      if (existingEntry) {
          existingEntry[item.group] = item.value;
      } else {
          const newEntry = { date: item.date };
          newEntry[item.group] = item.value;
          result.push(newEntry);
      }
  });

  return result;
}

.csv data

If your data is in .csv format, you can translate it thanks to the csvParse() function of d3. I'll write a blogpost soon on how to deal with the csv format. Subscribe to the project to know when it is ready!

ToDoAdd some more hints on how to type those data objects

Component skeleton

The goal here is to create a StackedAreaGraph component that will be stored in a StackedAreaGraph.tsx file. This component requires 3 props to render: a width, a height, and some data.

The shape of the data is described above. The width and height will be used to render an svg element in the DOM, in which we will insert the stacked area graph.

To put it in a nutshell, that's the skeleton of our StackedAreaGraph component:

import * as d3 from "d3"; // we will need d3.js

type WideDataItem = {
  date: string;
} & { [key: string]: number }

type StackedAreaGraphProps = {
  width: number;
  height: number;
  data: WideDataItem[];
};

export const StackedAreaGraph = ({ width, height, data }: StackedAreaGraphProps) => {

  // read the data
  // find the list of groups to display
  // stack the data
  // build the shapes

  return (
    <div>
      <svg width={width} height={height}>
        // render all the shapes
      </svg>
    </div>
  );
};

It's fundamental to understand that with this code organization, d3.js will be used to prepare the SVG circle, but it's React that will render them in the return() statement. We won't use d3 methods like append that you can find in usual d3.js examples.

Stacking

The trickiest part of a stacked area chart creation is probably the stacking step.


Series are displayed one on top of each other and you have to compute their positions on the Y axis. Fortunately d3.js is here to the rescue with a d3.stack() function. This is what you need to do to stack your data:

→ Build a stack generator

d3.stack() returns a stack generator that we call stackSeries here. d3.stack() is a function that returns a function.

.keys() is used to pass the list of groups that we want to stack on top of each other. Those keys are the ones used in the input dataset described in the data section.

const stackSeries = d3
  .stack()
  .keys(["groupA", "groupB"])

// stackSeries is now a function that takes the kind of
// dataset above and stack the series

→ Use the generator

Now that this stack generator is available, we just have to run it on our dataset to get the stacked values

const series = stackSeries(data);

That's it. series contains the stacked values that we can transform in coordinates for the shapes we need to draw.

→ Output

The output has kind of an usual shape and it's important to understand how it's formatted since shapes will be drawn from it.

Our generated stacked series object is an array. It has 1 item per group in the dataset.
For each group, there are 3 things:

  • a key prop that provides the group key
  • a index prop that provides its index 🤷
  • Several arrays of length 2. Each array describes the position of the group for a timestamp. First item in the array provides the bottomposition, second item provides the top.
[
  // First group of the dataset: at the very bottom of the stack
  [
    [0, 38, data: {…}], // First timestamp of the dataset: shape goes from 0 to 38 on the Y axis
    [0, 16, data: {…}], // Second timestamp: shape goes from 0 to 16
    ...                 // 1 entry per timestamp
    key: 'groupA',      // group name
    index: 0            // index
  ],

   // Second group of the dataset on top of the first one
  [[38, 57, data: {…}], ..., key: 'groupB', index: 1],

  //Third group
  [[57, 72, data: {…}], ..., key: 'groupC', index: 2],
  ...
]

Basic stacked area chart

The series object described above has all the information we need to draw a stacked area chart. We can loop through it and draw a path for each group, one by one.

Note that for each group the area() function of d3 is invoked. The usage of this function is deeply described in the area section of the gallery.

Area chart section


Here is a minimal code example wrapping the whole process.

basic stacked area chart with react and d3.js

Responsive Stacked Area with react

The component above is not responsive. It expects 2 props called width and height and will render a Stacked Area of those dimensions.

Making the Stacked Area responsive requires adding a wrapper component that gets the dimension of the parent div, and listening to a potential dimension change. This is possible thanks to a hook called useDimensions that will do the job for us.

useDimensions: a hook to make your viz responsive
export const useDimensions = (targetRef: React.RefObject<HTMLDivElement>) => {

  const getDimensions = () => {
    return {
      width: targetRef.current ? targetRef.current.offsetWidth : 0,
      height: targetRef.current ? targetRef.current.offsetHeight : 0
    };
  };

  const [dimensions, setDimensions] = useState(getDimensions);

  const handleResize = () => {
    setDimensions(getDimensions());
  };

  useEffect(() => {
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  useLayoutEffect(() => {
    handleResize();
  }, []);

  return dimensions;
}

I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.




Stacked Area inspiration

If you're looking for inspiration to create your next Stacked Area, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!

dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Stacked Area looks good!

visit

Offset and Curve types

Stacked area charts can easily be customized to use other offset and smoothing algorithm. This process can be used to create streamgraphs which are a varation of the stacked area graph.

The offset type controls how the data are stacked. You can read about the offset options available in the official documentation or play with the little example below.

The curve type controls how the smoothing of each shape is made. There are a myriad of options described in the official documentation.

Offset typeCurve type
246810

Try d3.js various options to offset the data and smooth shapes. See a smooth transition between options.

The animation uses react-spring to run. I'll publish a full blogpost on the topic soon!

Get notified


Evolution

Contact

👋 Hey, I'm Yan and I'm currently working on this project!

Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!