Chord diagram

Dataviz logo representing a Chord chart.

A chord diagram represents flows between several entities called nodes. Each node is represented by a fragment on the outer part of the circular layout. Then, arcs are drawn between each entities. The size of the arc is proportional to the importance of the flow..

Building a chord diagram with React and D3.js relies on the d3-chord module that computes the node and arc positions for us. React can then be used to draw everything in SVG. This page is a step by step tutorial with code sandboxes. It will teach you how to build a ChordDiagram component.

Useful links

The Data

The dataset required to build a chord diagram is a square matrix. It has a dimension of n x n where n is the number of nodes.

In javascript, this matrix is represented as an array of n array. Each individual array also has n items. The matrix of flow has a direction: the second item of the third row gives the flow from element 2 to element 3.

Usually an additional array is provided, giving the name of each node.


Here is a minimal example of the data structure:

// matrix of flow
const data = [
  [11975,  0, 8916, 2868],
  [ 1951, 10048, 2060, 6171],
  [ 0, 16145, 8090, 8045],
  [ 1013,   990,  940, 6907]
];

// node names
const nodeNames = ['Barcelona', 'Paris', 'Dakar', 'NY']

Component skeleton

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

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 chord diagram.

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

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

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

export const ChordDiagram = ({ width, height, data }: ChordDiagramProps) => {

  // read the data
  // compute the nodes and ribbon position
  // build the arcs
  // build the ribbon

  return (
    <div>
      <svg width={width} height={height}>
        // render all the arcs and ribbons
      </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.

The chord() function

We need to transform the flow matrix described in the data section into a list of node and connection coordinates. This is easy thanks to the chord() function of d3.js.

The function can be used as follow:

const chordGenerator = d3
.chord()
.padAngle(0.05) // padding between nodes
.sortSubgroups(d3.descending);

const chord = chordGenerator(data);

The returned object (chord in this example) is an array listing all the connections. For each, details about the source and the target are provided:

[
  // first connection: flow between node 1 and node 1
  {
    source: { index: 0, startAngle: 0, endAngle: 0.84, value: 11975 },
    target: { index: 0, startAngle: 0, endAngle: 0.84, value: 11975
    }
  },
  // second connection: flow between node 2 and node 1
  {
    source: { index: 1, startAngle: 3.01, endAngle: 3.15, value: 1951 },
    target: { index: 1, startAngle: 1.67, endAngle: 1.67, value: 0
    }
  },
  // ...
]

Last but not least, the array also has a group property with details about all nodes of the chord diagram.

chord() official documentation

Draw the nodes

Nodes are drawn using the group property of the chord object computed above. For each group, the start and end angles are provided.

From this information it is possible to draw an arc thanks to the arc() function of d3. It is exactly the same process as for a donut chart. Please visit the donut section of the gallery for more explanation!

Donut section

Nodes are drawn thanks to the arc() function of d3.js, like for a donut chart.

Draw the connections

We now have to draw the connections between nodes that are listed in the initial square matrix (data).

The connection coordinates are listed in the chord object computed in the previous section. For each connection we know the startAngle and endAngle of the source and of thetarget.

This is everything we need to compute the connections thanks to the ribbon() function of d3 as follow:

const allConnections = chord.map((connection, i) => {
  const d = ribbonGenerator(connection);
  return <path key={i} d={d} />;
});

Connections between nodes are drawn thanks to the ribbon() function of d3.js.

d3.ribbon() documentation

Responsive Chord Diagram with react

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

Making the Chord Diagram 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.




Chord Diagram inspiration

If you're looking for inspiration to create your next Chord Diagram, 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 Chord Diagram looks good!

visit

First chord diagram

I suggest 2 improvements to get a descent chord diagram:

→ Colors

Pretty straightforward to implement. You just need to create an array of colors. Then, for each item to draw the index is always available. It can be used to retrieve the color in the color array.

→ Labels

A new prop needs to be passed to the component with a list of names for the nodes. I suggest to position labels as for a donut chart but many other possibilities are available.

Connections between nodes are drawn thanks to the ribbon() function of d3.js.

ToDoAdd section on hover effect
ToDoTalk about chordDirected() and chordTranspose()

Flow

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!