Stacks side by side: each year, North America stacks on Europe in one column, and Latin America, the Middle East and Africa stack on Asia in the next. Two groups, and what each is made of.
Combine the two when your series fall into groups: the clusters compare the groups, the stacks show what each is made of. Readers need the legend to tell which series belongs to which stack, so keep the groups few and obvious.
Built with amCharts 5, a JavaScript library. The JavaScript includes your theme and renderer (canvas or SVG); the JSON also follows the changes you make with the controls.
npm install @amcharts/amcharts5. See Getting started.
The same chart as a JSON config. Chart Maker reads and writes this format, the library's JSON parser turns it into a chart, and the <am5-chart> element puts it on a page with no code."type" is the class to create and "settings" what you would pass to its new() in code; "properties" fill in its parts, lists such as series and axes or templates such as columns.template; "refs" holds objects used in more than one place ("#series-0" stands for the one named series-0); a string starting with "@" takes a value from another object, such as "@series.get('fill')".
A complete page. Save it as an .html file and open it in a browser.
Paste this into an AI coding assistant to build the chart from scratch. More in amCharts & AI.
// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("chartdiv");
// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([
am5themes_Animated.new(root),
am5themes_Responsive.new(root)
]);
// Create chart
// https://www.amcharts.com/docs/v5/charts/xy-chart/
var chart = root.container.children.push(am5xy.XYChart.new(root, {
panX: false, // no panning by drag
panY: false,
wheelX: "panX", // a horizontal wheel or trackpad swipe pans...
wheelY: "zoomX", // ...and the vertical wheel zooms in on the years
paddingLeft: 0, // no gap at the chart's left edge
layout: root.verticalLayout // the legend goes below the plot
}));
// Add legend
// https://www.amcharts.com/docs/v5/charts/xy-chart/legend-xy-series/
var legend = chart.children.push(am5.Legend.new(root, {
centerX: am5.p50, // centered...
x: am5.p50 // ...under the chart
}));
// no value labels: without them the items stay compact, so the legend sits centered under the chart
legend.valueLabels.template.set("forceHidden", true);
var data = [{
"year": "2023",
"europe": 2.5,
"namerica": 2.5,
"asia": 2.1,
"lamerica": 1,
"meast": 0.8,
"africa": 0.4
}, {
"year": "2024",
"europe": 2.6,
"namerica": 2.7,
"asia": 2.2,
"lamerica": 0.5,
"meast": 0.4,
"africa": 0.3
}, {
"year": "2025",
"europe": 2.8,
"namerica": 2.9,
"asia": 2.4,
"lamerica": 0.3,
"meast": 0.9,
"africa": 0.5
}];
// Create axes
// https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
var xRenderer = am5xy.AxisRendererX.new(root, {
// each year's columns take the middle 80% of its cell, leaving a gap between years
cellStartLocation: 0.1,
cellEndLocation: 0.9
});
var xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
categoryField: "year",
renderer: xRenderer,
tooltip: am5.Tooltip.new(root, {})
}));
xRenderer.grid.template.setAll({
location: 1 // grid lines between the years, not through the middle of each
})
xAxis.data.setAll(data);
var yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
min: 0, // columns start at zero
renderer: am5xy.AxisRendererY.new(root, {
strokeOpacity: 0.1 // a faint axis line
})
}));
// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
// adds one region's columns; with stacked true they go on top of the series before
function makeSeries(name, fieldName, stacked) {
var series = chart.series.push(am5xy.ColumnSeries.new(root, {
stacked: stacked,
name: name,
xAxis: xAxis,
yAxis: yAxis,
valueYField: fieldName,
categoryXField: "year"
}));
series.columns.template.setAll({
tooltipText: "{name}, {categoryX}: {valueY}", // hover a column for its region, year and value
width: am5.percent(90), // each column takes 90% of its slot
tooltipY: am5.percent(10), // the tooltip points near the column's top
// a soft shadow under each column
shadowColor: am5.color(0x000000),
shadowOpacity: 0.3, // 30% opaque...
shadowBlur: 6, // ...blurred 6px...
shadowOffsetX: 2, // ...2px to the right...
shadowOffsetY: 2 // ...and 2px down
});
series.data.setAll(data);
// Make stuff animate on load
// https://www.amcharts.com/docs/v5/concepts/animations/
series.appear();
// the value in the middle of each column
series.bullets.push(function () {
return am5.Bullet.new(root, {
locationY: 0.5, // halfway up the column
sprite: am5.Label.new(root, {
text: "{valueY}",
fill: root.interfaceColors.get("alternativeText"), // the theme's text color for use on colored fills
centerY: am5.percent(50), // centered on its point...
centerX: am5.percent(50), // ...both ways
populateText: true // fill in {valueY} from the data
})
});
});
// hide the label of a segment too short to hold it; its tooltip still shows the value
series.columns.template.onPrivate("height", function (height, target) {
am5.array.each(target.dataItem.bullets || [], function (bullet) {
if (height > 20) { // 20px or taller: room for the label
bullet.get("sprite").show();
} else {
bullet.get("sprite").hide();
}
});
});
legend.data.push(series);
}
// a series with stacked false starts a new column; a stacked one goes on top of the one before
makeSeries("Europe", "europe", false);
makeSeries("North America", "namerica", true);
makeSeries("Asia", "asia", false);
makeSeries("Latin America", "lamerica", true);
makeSeries("Middle East", "meast", true);
makeSeries("Africa", "africa", true);
// Make stuff animate on load
// https://www.amcharts.com/docs/v5/concepts/animations/
chart.appear(1000, 100);
{
"type": "XYChart",
"settings": {
"panX": false,
"panY": false,
"wheelX": "panX",
"wheelY": "zoomX",
"paddingLeft": 0,
"layout": {
"type": "VerticalLayout"
}
},
"properties": {
"series": [
"#series-0",
"#series-1",
"#series-2",
"#series-3",
"#series-4",
"#series-5"
],
"xAxes": [
"#axis-0"
],
"yAxes": [
"#axis-1"
]
},
"children": [
{
"type": "Legend",
"settings": {
"centerX": {
"type": "Percent",
"value": 50
},
"x": {
"type": "Percent",
"value": 50
}
},
"properties": {
"data": [
"#series-0",
"#series-1",
"#series-2",
"#series-3",
"#series-4",
"#series-5"
],
"valueLabels": {
"properties": {
"template": {
"settings": {
"forceHidden": true
}
}
}
}
}
}
],
"refs": [
{
"data-0": [
{
"year": "2023",
"europe": 2.5,
"namerica": 2.5,
"asia": 2.1,
"lamerica": 1,
"meast": 0.8,
"africa": 0.4
},
{
"year": "2024",
"europe": 2.6,
"namerica": 2.7,
"asia": 2.2,
"lamerica": 0.5,
"meast": 0.4,
"africa": 0.3
},
{
"year": "2025",
"europe": 2.8,
"namerica": 2.9,
"asia": 2.4,
"lamerica": 0.3,
"meast": 0.9,
"africa": 0.5
}
]
},
{
"axis-0": {
"type": "CategoryAxis",
"settings": {
"categoryField": "year",
"renderer": {
"type": "AxisRendererX",
"settings": {
"cellStartLocation": 0.1,
"cellEndLocation": 0.9
},
"properties": {
"grid": {
"properties": {
"template": {
"settings": {
"location": 1
}
}
}
}
}
},
"tooltip": {
"type": "Tooltip"
}
},
"properties": {
"data": "#data-0"
}
}
},
{
"axis-1": {
"type": "ValueAxis",
"settings": {
"min": 0,
"renderer": {
"type": "AxisRendererY",
"settings": {
"strokeOpacity": 0.1
}
}
}
}
},
{
"series-0": {
"type": "ColumnSeries",
"settings": {
"stacked": false,
"name": "Europe",
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"valueYField": "europe",
"categoryXField": "year"
},
"properties": {
"data": "#data-0",
"columns": {
"properties": {
"template": {
"settings": {
"tooltipText": "{name}, {categoryX}: {valueY}",
"width": {
"type": "Percent",
"value": 90
},
"tooltipY": {
"type": "Percent",
"value": 10
},
"shadowColor": {
"type": "Color",
"value": "#000000"
},
"shadowOpacity": 0.3,
"shadowBlur": 6,
"shadowOffsetX": 2,
"shadowOffsetY": 2
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"locationY": 0.5,
"sprite": {
"type": "Label",
"settings": {
"text": "{valueY}",
"fill": "@root.interfaceColors.get('alternativeText')",
"centerY": {
"type": "Percent",
"value": 50
},
"centerX": {
"type": "Percent",
"value": 50
},
"populateText": true
}
}
}
}
]
}
}
},
{
"series-1": {
"type": "ColumnSeries",
"settings": {
"stacked": true,
"name": "North America",
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"valueYField": "namerica",
"categoryXField": "year"
},
"properties": {
"data": "#data-0",
"columns": {
"properties": {
"template": {
"settings": {
"tooltipText": "{name}, {categoryX}: {valueY}",
"width": {
"type": "Percent",
"value": 90
},
"tooltipY": {
"type": "Percent",
"value": 10
},
"shadowColor": {
"type": "Color",
"value": "#000000"
},
"shadowOpacity": 0.3,
"shadowBlur": 6,
"shadowOffsetX": 2,
"shadowOffsetY": 2
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"locationY": 0.5,
"sprite": {
"type": "Label",
"settings": {
"text": "{valueY}",
"fill": "@root.interfaceColors.get('alternativeText')",
"centerY": {
"type": "Percent",
"value": 50
},
"centerX": {
"type": "Percent",
"value": 50
},
"populateText": true
}
}
}
}
]
}
}
},
{
"series-2": {
"type": "ColumnSeries",
"settings": {
"stacked": false,
"name": "Asia",
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"valueYField": "asia",
"categoryXField": "year"
},
"properties": {
"data": "#data-0",
"columns": {
"properties": {
"template": {
"settings": {
"tooltipText": "{name}, {categoryX}: {valueY}",
"width": {
"type": "Percent",
"value": 90
},
"tooltipY": {
"type": "Percent",
"value": 10
},
"shadowColor": {
"type": "Color",
"value": "#000000"
},
"shadowOpacity": 0.3,
"shadowBlur": 6,
"shadowOffsetX": 2,
"shadowOffsetY": 2
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"locationY": 0.5,
"sprite": {
"type": "Label",
"settings": {
"text": "{valueY}",
"fill": "@root.interfaceColors.get('alternativeText')",
"centerY": {
"type": "Percent",
"value": 50
},
"centerX": {
"type": "Percent",
"value": 50
},
"populateText": true
}
}
}
}
]
}
}
},
{
"series-3": {
"type": "ColumnSeries",
"settings": {
"stacked": true,
"name": "Latin America",
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"valueYField": "lamerica",
"categoryXField": "year"
},
"properties": {
"data": "#data-0",
"columns": {
"properties": {
"template": {
"settings": {
"tooltipText": "{name}, {categoryX}: {valueY}",
"width": {
"type": "Percent",
"value": 90
},
"tooltipY": {
"type": "Percent",
"value": 10
},
"shadowColor": {
"type": "Color",
"value": "#000000"
},
"shadowOpacity": 0.3,
"shadowBlur": 6,
"shadowOffsetX": 2,
"shadowOffsetY": 2
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"locationY": 0.5,
"sprite": {
"type": "Label",
"settings": {
"text": "{valueY}",
"fill": "@root.interfaceColors.get('alternativeText')",
"centerY": {
"type": "Percent",
"value": 50
},
"centerX": {
"type": "Percent",
"value": 50
},
"populateText": true
}
}
}
}
]
}
}
},
{
"series-4": {
"type": "ColumnSeries",
"settings": {
"stacked": true,
"name": "Middle East",
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"valueYField": "meast",
"categoryXField": "year"
},
"properties": {
"data": "#data-0",
"columns": {
"properties": {
"template": {
"settings": {
"tooltipText": "{name}, {categoryX}: {valueY}",
"width": {
"type": "Percent",
"value": 90
},
"tooltipY": {
"type": "Percent",
"value": 10
},
"shadowColor": {
"type": "Color",
"value": "#000000"
},
"shadowOpacity": 0.3,
"shadowBlur": 6,
"shadowOffsetX": 2,
"shadowOffsetY": 2
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"locationY": 0.5,
"sprite": {
"type": "Label",
"settings": {
"text": "{valueY}",
"fill": "@root.interfaceColors.get('alternativeText')",
"centerY": {
"type": "Percent",
"value": 50
},
"centerX": {
"type": "Percent",
"value": 50
},
"populateText": true
}
}
}
}
]
}
}
},
{
"series-5": {
"type": "ColumnSeries",
"settings": {
"stacked": true,
"name": "Africa",
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"valueYField": "africa",
"categoryXField": "year"
},
"properties": {
"data": "#data-0",
"columns": {
"properties": {
"template": {
"settings": {
"tooltipText": "{name}, {categoryX}: {valueY}",
"width": {
"type": "Percent",
"value": 90
},
"tooltipY": {
"type": "Percent",
"value": 10
},
"shadowColor": {
"type": "Color",
"value": "#000000"
},
"shadowOpacity": 0.3,
"shadowBlur": 6,
"shadowOffsetX": 2,
"shadowOffsetY": 2
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"locationY": 0.5,
"sprite": {
"type": "Label",
"settings": {
"text": "{valueY}",
"fill": "@root.interfaceColors.get('alternativeText')",
"centerY": {
"type": "Percent",
"value": 50
},
"centerX": {
"type": "Percent",
"value": 50
},
"populateText": true
}
}
}
}
]
}
}
}
]
}