Introduction
This article demonstrates how to create and use sankey chart using vega in reactjs. This article starts with the introduction of the react-vega package. After that, it demonstrates how vega json file works for creating any chart using its json formatted data.
What is Vega?
Vega is a visualization grammar, a declarative language for creating, saving, and sharing interactive visualization designs.With Vega, you can describe the visual appearance and interactive behavior of a visualization in a JSON format, and generate web-based views using Canvas or SVG.
Reference from:Learn more about Vega from here
To achieve this feature we will use a npm package npm i react-vega using this you can modify the json file and create it according to your requirement.
Prerequisites
- Basic knowledge of ReactJS
- Visual Studio Code
- Node and NPM installed
Step 1. Install NPM dependencies Create a React.js Project
Let's create a new React project by using the following command.
npx create-react-app react-sankeychart
Step 2. Install NPM dependencies
npm i react-vega
Step 3. Creating a Component
Now go to the src folder and create a new folder for sankey chart and inside in it create a component, 'sankey-chart.js'. Add the following code to this component.
import React from 'react';
import { Vega } from 'react-vega';
import sankychart from './sankychart.json';
export const Sankychart = () => {
const spec ={
"$schema": "https://vega.github.io/schema/vega/v5.2.json",
"height": 400,
"width": 600,
"data": [
{
"name": "rawData",
"values": sankychart,
"transform": [
{
"type": "formula",
"expr": "datum['organizationReference.legalName']",
"as": "stk1"
},
{
"type": "formula",
"expr": "datum.municipality",
"as": "stk2"
},
{
"type": "formula",
"expr": "datum.sum_amount",
"as": "size"
}],
"transform": [
{
"type": "formula",
"expr": "datum['organizationReference.legalName']",
"as": "stk1"
},
{
"type": "formula",
"expr": "datum.municipality",
"as": "stk2"
},
{
"type": "formula",
"expr": "datum.sum_amount",
"as": "size"
}
]
},
{
"name": "nodes",
"source": "rawData",
"transform": [
{
"type": "filter",
"expr": "!groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2"
},
{
"type": "formula",
"expr": "datum.stk1+datum.stk2",
"as": "key"
},
{
"type": "fold",
"fields": [
"stk1",
"stk2"
],
"as": [
"stack",
"grpId"
]
},
{
"type": "formula",
"expr": "datum.stack == 'stk1' ? datum.stk1+' '+datum.stk2 : datum.stk2+' '+datum.stk1",
"as": "sortField"
},
{
"type": "stack",
"groupby": [
"stack"
],
"sort": {
"field": "sortField",
"order": "descending"
},
"field": "size"
},
{
"type": "formula",
"expr": "(datum.y0+datum.y1)/2",
"as": "yc"
}
]
},
{
"name": "groups",
"source": "nodes",
"transform": [
{
"type": "aggregate",
"groupby": [
"stack",
"grpId"
],
"fields": [
"size"
],
"ops": [
"sum"
],
"as": [
"total"
]
},
{
"type": "stack",
"groupby": [
"stack"
],
"sort": {
"field": "grpId",
"order": "descending"
},
"field": "total"
},
{
"type": "formula",
"expr": "scale('y', datum.y0)",
"as": "scaledY0"
},
{
"type": "formula",
"expr": "scale('y', datum.y1)",
"as": "scaledY1"
},
{
"type": "formula",
"expr": "datum.stack == 'stk1'",
"as": "rightLabel"
},
{
"type": "formula",
"expr": "datum.total/domain('y')[1]",
"as": "percentage"
}
]
},
{
"name": "destinationNodes",
"source": "nodes",
"transform": [
{
"type": "filter",
"expr": "datum.stack == 'stk2'"
}
]
},
{
"name": "edges",
"source": "nodes",
"transform": [
{
"type": "filter",
"expr": "datum.stack == 'stk1'"
},
{
"type": "lookup",
"from": "destinationNodes",
"key": "key",
"fields": [
"key"
],
"as": [
"target"
]
},
{
"type": "linkpath",
"orient": "horizontal",
"shape": "diagonal",
"sourceY": {
"expr": "scale('y', datum.yc)"
},
"sourceX": {
"expr": "scale('x', 'stk1') + bandwidth('x')"
},
"targetY": {
"expr": "scale('y', datum.target.yc)"
},
"targetX": {
"expr": "scale('x', 'stk2')"
}
},
{
"type": "formula",
"expr": "range('y')[0]-scale('y', datum.size)",
"as": "strokeWidth"
},
{
"type": "formula",
"expr": "datum.size/domain('y')[1]",
"as": "percentage"
}
]
}
],
"scales": [
{
"name": "x",
"type": "band",
"range": "width",
"domain": [
"stk1",
"stk2"
],
"paddingOuter": 0.05,
"paddingInner": 0.95
},
{
"name": "y",
"type": "linear",
"range": "height",
"domain": {
"data": "nodes",
"field": "y1"
}
},
{
"name": "color",
"type": "ordinal",
"range": [
"#98df8a",
"#27acaa",
"#ff810a",
"#34c668",
"#178be4",
"#714ac3",
"#27acaa",
"#8ce8ad",
"#ffbb78",
"#c4c4cd",
"#d62728"
],
"domain": {"data": "rawData", "field": "stk1"}
},
{
"name": "stackNames",
"type": "ordinal",
"range": ["Organization",
"Municipality"],
"domain": ["stk1", "stk2"]
}
],
"axes": [
{
"orient": "bottom",
"scale": "x",
"domain" : false,
"ticks" : false,
"labelPadding" : 20,
"encode": {
"labels": {
"update": {
"text": {
"scale": "stackNames",
"field": "value",
"fontWeight" : "bold",
"fontSize" : 14
}
}
}
}
},
{
"orient": "top",
"scale": "x",
"domain" : false,
"ticks" : false,
"labelPadding" : 20,
"encode": {
"labels": {
"update": {
"text": {
"scale": "stackNames",
"field": "value",
"fontWeight" : "bold",
"fontSize" : 14
}
}
}
}
},
{
"orient": "left",
"scale": "y",
"labels" : false,
"domain" : false,
"ticks" : false
}
],
"marks": [
{
"type": "path",
"name": "edgeMark",
"from": {"data": "edges"},
"clip": true,
"encode": {
"update": {
"stroke": [
{
"test": "groupSelector && groupSelector.stack=='stk1'",
"scale": "color",
"field": "stk2"
},
{"scale": "color", "field": "stk1"}
],
"strokeWidth": {"field": "strokeWidth"},
"path": {"field": "path"},
"strokeOpacity": {
"signal": "!groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3"
},
"zindex": {
"signal": "!groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0"
},
"tooltip": {
"signal": "datum.stk1 + ' → ' + datum.stk2 + ' ' + format(datum.size, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'"
}
},
"hover": {"strokeOpacity": {"value": 1}}
}
},
{
"type": "rect",
"name": "groupMark",
"from": {"data": "groups"},
"encode": {
"enter": {
"fill": {"scale": "color", "field": "grpId"},
"width": {"scale": "x", "band": 1}
},
"update": {
"x": {"scale": "x", "field": "stack"},
"y": {"field": "scaledY0"},
"y2": {"field": "scaledY1"},
"fillOpacity": {"value": 0.6},
"tooltip": {
"signal": "datum.grpId + ' ' + format(datum.total, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'"
}
},
"hover": {"fillOpacity": {"value": 1}}
}
},
{
"type": "text",
"from": {"data": "groups"},
"interactive": false,
"encode": {
"update": {
"x": {
"signal": "scale('x', datum.stack) + (datum.rightLabel ? bandwidth('x') + 8 : -8)"
},
"yc": {"signal": "(datum.scaledY0 + datum.scaledY1)/2"},
"align": {"signal": "datum.rightLabel ? 'left' : 'right'"},
"baseline": {"value": "middle"},
"fontWeight": {"value": "bold"},
"text": {
"signal": "abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : ''"
}
}
}
},
{
"type": "group",
"data": [
{
"name": "dataForShowAll",
"values": [{}],
"transform": [{"type": "filter", "expr": "groupSelector"}]
}
],
"encode": {
"enter": {
"xc": {"signal": "width/2"},
"y": {"value": 30},
"width": {"value": 80},
"height": {"value": 30}
}
},
"marks": [
{
"type": "group",
"name": "groupReset",
"from": {"data": "dataForShowAll"},
"encode": {
"enter": {
"cornerRadius": {"value": 6},
"fill": {"value": "#f5f5f5"},
"stroke": {"value": "#c1c1c1"},
"strokeWidth": {"value": 2},
"height": {"field": {"group": "height"}},
"width": {"field": {"group": "width"}}
},
"update": {"opacity": {"value": 1}},
"hover": {"opacity": {"value": 0.7}}
},
"marks": [
{
"type": "text",
"interactive": false,
"encode": {
"enter": {
"xc": {"field": {"group": "width"}, "mult": 0.5},
"yc": {
"field": {"group": "height"},
"mult": 0.5,
"offset": 2
},
"align": {"value": "center"},
"baseline": {"value": "middle"},
"fontWeight": {"value": "bold"},
"text": {"value": "Show All"}
}
}
}
]
}
]
}
],
"signals": [
{
"name": "groupHover",
"value": {},
"on": [
{
"events": "@groupMark:mouseover",
"update": "{stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}"
},
{"events": "mouseout", "update": "{}"}
]
},
{
"name": "groupSelector",
"value": false,
"on": [
{
"events": "@groupMark:click!",
"update": "{stack:datum.stack, stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}"
},
{
"events": [
{"type": "click", "markname": "groupReset"},
{"type": "dblclick"}
],
"update": "false"
}
]
}
]
}
return (
<>
<Vega spec={spec} actions={false} />
</>
);
};
export default Sankychart;
Step 3. Create a new component for sortable list
Create a new JSON file, 'sankrychart.json'. Add the following code to this file.
[{
"organizationReference.legalName": "Autolinee Caivano",
"municipality": "Picerno",
"sum_amount": 251830
},
{
"organizationReference.legalName": "P&C Consorzio Stabile arl",
"municipality": "Pietragalla",
"sum_amount": 90856
},
{
"organizationReference.legalName": "Pepice Nicola",
"municipality": "San Fele",
"sum_amount": 119040
},
{
"organizationReference.legalName": "F.lli Martoccia",
"municipality": "Pietragalla",
"sum_amount": 213877
},
{
"organizationReference.legalName": "Daraio Pasquale",
"municipality": "Baragiano",
"sum_amount": 56298
},
{
"organizationReference.legalName": "Inno srls",
"municipality": "Ruvo del Monte",
"sum_amount": 6393
},
{
"organizationReference.legalName": "3N Costruzioni",
"municipality": "Pietrapertosa",
"sum_amount": 83291
},
{
"organizationReference.legalName": "Esteja Soc. Coop",
"municipality": "Tolve",
"sum_amount": 527485
},
{
"organizationReference.legalName": "C.C.D. Costruzioni e manutenzioni srl",
"municipality": "Baragiano",
"sum_amount": 239217
},
{
"organizationReference.legalName": "Costruzioni Messina Soc. Coop.",
"municipality": "Balvano",
"sum_amount": 70799
},
]
Step 4. Add the below code in App.js file
import './App.css';
import Sankychart from './sanky-Chart/sankychart';
function App() {
return (
<Sankychart/>
);
}
export default App;
Step 5. Output
Now, run the project by using the 'npm start' command, and check the result.
Summary
In this article, we learned how to create a sankey chart using Vega library in ReactJS application.