So I am trying to create something like above image with ApexCharts. Please show me how to achieve the bar chart I have shown in image.
I tried several different combinations with series data. But none of them work. For example in this link: https://apexcharts.com/javascript-chart-demos/column-charts/grouped-stacked-columns/
They have shown good example but it doesn't work with my case.
{
name: 'Car',
group: '2023',
data: [44000, 55000, 41000]
}
They have given objects like above. But the issue is the name
field needs to be unique. So I had to do Car1, Car2, Truck1, Truck2. That added 4 fields in legend which is wrong. Also I couldn't find a way to show year text below the bar.
Think that the configuration for Apex Charts below should be closely meet your requirement (not 100%).
Concepts:
For each group, you must have the unique name
so that it will perform the stacking correctly. Without it, the generated bar will overlap to other bar that in the same group.
To prevent generating the legend items such as "Car 1", "Car 2" and etc, you can apply legend.customLegendItems
with ['Truck', 'Car']
. However, it will sacrifice the effect from the legend item when hovering the legend item to focus the respective series; and clicking the legend item to select/unselect the respective series from the chart.
To show multiple labels for month and year categories in the x-axis, you can work with xaxis.categories
and xaxis.group.groups
. However, you need additional handling to conrol generating the element for years so that it will display in 1-line in SVG (Although the way I did is not so dynamic and place the label centrally in respective bar).
const options = {
series: [
{
name: 'Truck 1',
group: '2023',
data: [
{ x: 'Jan-2023', y: 44000 },
{ x: 'Feb-2023', y: 55000 },
{ x: 'Mar-2023', y: 41000 },
],
},
{
name: 'Car 1',
group: '2023',
data: [
{ x: 'Jan-2023', y: 13000 },
{ x: 'Feb-2023', y: 36000 },
{ x: 'Mar-2023', y: 20000 },
],
},
{
name: 'Truck 2',
group: '2024',
data: [
{ x: 'Jan-2024', y: 48000 },
{ x: 'Feb-2024', y: 50000 },
{ x: 'Mar-2024', y: 40000 },
],
},
{
name: 'Car 2',
group: '2024',
data: [
{ x: 'Jan-2024', y: 20000 },
{ x: 'Feb-2024', y: 40000 },
{ x: 'Mar-2024', y: 25000 },
],
},
],
chart: {
type: 'bar',
height: 400,
stacked: true,
},
stroke: {
width: 1,
colors: ['#fff'],
},
dataLabels: {
formatter: (val) => {
return val / 1000 + 'K';
},
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '70%',
},
},
xaxis: {
categories: [
['2023', '2024'],
['2023', '2024'],
['2023', '2024'],
],
type: 'category',
group: {
style: {
fontSize: '10px',
fontWeight: 700,
},
groups: [
{ title: 'Jan', cols: 1 },
{ title: 'Feb', cols: 1 },
{ title: 'Mar', cols: 1 },
],
},
},
fill: {
opacity: 1,
},
colors: ['#008FFB', '#00E396'],
yaxis: {
labels: {
formatter: (val) => {
return val / 1000 + 'K';
},
},
},
legend: {
position: 'top',
horizontalAlign: 'left',
customLegendItems: ['Truck', 'Car'],
markers: {
fillColors: ['#008FFB', '#00E396'],
},
},
};
const chart = new ApexCharts(document.querySelector('#chart'), options);
chart.render().then(() => {
setTimeout(() => {
const svg = document.querySelector('.apexcharts-svg');
const chartWidth = svg?.getBoundingClientRect().width;
const labels = document.querySelectorAll('.apexcharts-xaxis-label');
if (!chartWidth || labels.length < 2) return;
// Estimate spacing dynamically based on distance between label centers
const x1 = parseFloat(
labels[0].getAttribute('x') ||
labels[0].querySelector('tspan')?.getAttribute('x')
);
const x2 = parseFloat(
labels[1].getAttribute('x') ||
labels[1].querySelector('tspan')?.getAttribute('x')
);
const estimatedGroupWidth = x2 - x1; // Distance between label groups
// Assume 2 bars per group (adjust here if it's 3+)
const barsPerGroup = 2;
// Spacing between bars = total width / (bars per group)
const dynamicGroupSpacing = estimatedGroupWidth / barsPerGroup;
labels.forEach((textEl) => {
const tspans = textEl.querySelectorAll('tspan');
if (tspans.length === 2) {
const baseX = parseFloat(
textEl.getAttribute('x') || tspans[0].getAttribute('x')
);
// Align each tspan toward its corresponding bar
tspans[0].setAttribute('x', baseX - dynamicGroupSpacing / 2 + 15);
tspans[1].setAttribute('x', baseX + dynamicGroupSpacing / 2 - 20);
tspans[0].removeAttribute('dy');
tspans[1].removeAttribute('dy');
}
});
}, 100);
});