A Sankey graph is a powerful data visualization that show how elements flow from one state to another in both state and quantity.

In this above graph, we can see how data flows from one state to another, with wider bands indicating a larger quantity.

As with most things, we start with a data set. In this case, we have a list of nodes and a list links. A node needs to, at least, have an index or id, or some property to uniquely identify the node. Links have three properties: a source, a target, and a value. The source and target are pointers to the appropriate nodes. The value is the number of items that traveled that path.

let data = {
    nodes: [
        { id: "A1" },
        { id: "A2" },
        /* snip */
    ],
    links = [
        { source: "A1", target: "B1", value: 27 }
    ]
};

Next, we create a sankey instance. This creates a function that can be used to generate our Sankey data.

const sankey = d3.sankey()
                 .size([width, height])
                 .nodeId(d => d.id)
                 .nodeWidth(20)
                 .nodePadding(10)
                 .nodeAlign(d3.sankeyCenter);
let graph = sankey(data);

Next, we can draw the nodes and the links. Links are SVG paths, and nodes are SVG rectangles.

let links = svg.append("g")
               .classed("links", true)
               .selectAll("path")
               .data(graph.links)
               .enter()
               .append("path")
               .classed("link", true)
               .attr("d", d3.sankeyLinkHorizontal())
               .attr("fill", "none")
               .attr("stroke", "#606060")
               .attr("stroke-width", d => d.width)
               .attr("stoke-opacity", 0.5);

let nodes = svg.append("g")
               .classed("nodes", true)
               .selectAll("rect")
               .data(graph.nodes)
               .enter()
               .append("rect")
               .classed("node", true)
               .attr("x", d => d.x0)
               .attr("y", d => d.y0)
               .attr("width", d => d.x1 - d.x0)
               .attr("height", d => d.y1 - d.y0)
               .attr("fill", "blue")
               .attr("opacity", 0.8);

That’s the basics of how a Sankey graph works in D3. Any other features, including moving the nodes or applying colors, is beyond the scope of this blog post. The full code for the graph in this document is available here.

Note: this blog entry applies to the following software versions.

  1. D3.js @ 5.4.0
  2. D3 Sankey @ 0.7.1