Traffic by Country in JavaScript
Using ApexCharts with JavaScript
This Traffic by Country example uses ApexCharts.js directly in JavaScript, with no wrapper component.
Install it with npm install apexcharts, then mount the chart with new ApexCharts(element, options).render().
// Twenty countries, sixty days of sessions each, on ONE shared y scale, so
// the long tail reads honestly (flat means small, not "own scale"). The
// column count comes from the container's own width and `minPanelWidth`
// alone: 4 -> 3 -> 2 -> 1 as the container narrows. The chart card has a CSS
// resize handle (bottom-right corner), so drag it: the trellis's single
// ResizeObserver recolumns the live panels without re-creating them.
//
// Why not a top-5 chart with an "other" bucket? Because the tail is where
// the anomalies live: watch New Zealand spike mid-series.
//
// Deterministic walks so the e2e snapshot is stable.
function mulberry32(seed) {
return function () {
seed |= 0
seed = (seed + 0x6d2b79f5) | 0
var t = Math.imul(seed ^ (seed >>> 15), 1 | seed)
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
var COUNTRIES = [
{ name: 'United States', seed: 5, base: 48000 },
{ name: 'India', seed: 11, base: 30500 },
{ name: 'United Kingdom', seed: 19, base: 22000 },
{ name: 'Germany', seed: 23, base: 18200 },
{ name: 'Brazil', seed: 31, base: 15400 },
{ name: 'France', seed: 37, base: 13100 },
{ name: 'Canada', seed: 43, base: 12000 },
{ name: 'Australia', seed: 47, base: 9600 },
{ name: 'Japan', seed: 59, base: 8100 },
{ name: 'Netherlands', seed: 61, base: 6200 },
{ name: 'Spain', seed: 71, base: 5100 },
{ name: 'Mexico', seed: 79, base: 4300 },
{ name: 'Sweden', seed: 83, base: 3600 },
{ name: 'Poland', seed: 89, base: 3000 },
{ name: 'Singapore', seed: 97, base: 2600 },
{ name: 'South Africa', seed: 101, base: 2200 },
{ name: 'Argentina', seed: 107, base: 1800 },
{ name: 'Ireland', seed: 113, base: 1500 },
{ name: 'Thailand', seed: 127, base: 1200 },
{ name: 'New Zealand', seed: 131, base: 900 },
]
function dailySessions(seed, base, spikeAt) {
var rand = mulberry32(seed)
var out = []
var ts = new Date('01 Jan 2025').getTime()
var val = base
for (var i = 0; i < 60; i++) {
val = Math.max(base * 0.5, val + (rand() - 0.5) * base * 0.12)
var v = val
// The anomaly the top-5 chart would never show: a three-day viral spike.
if (spikeAt && i >= spikeAt && i < spikeAt + 3) {
v = val + 11000 * (1 - (i - spikeAt) * 0.3)
}
out.push([ts, Math.round(v)])
ts += 86400000
}
return out
}
var trafficSeries = COUNTRIES.map(function (c) {
return {
name: 'Sessions',
country: c.name,
data: dailySessions(c.seed, c.base, c.name === 'New Zealand' ? 34 : 0),
}
})
var options = {
series: trafficSeries,
chart: {
id: 'trafficTrellis',
type: 'area',
animations: {
enabled: false,
},
},
trellis: {
by: 'country',
minPanelWidth: 240,
panelHeight: 120,
gap: 12,
},
colors: ['#0369A1'],
stroke: {
width: 2,
curve: 'straight',
},
fill: {
type: 'gradient',
gradient: {
opacityFrom: 0.35,
opacityTo: 0.05,
},
},
xaxis: {
type: 'datetime',
},
yaxis: {
labels: {
formatter: function (val) {
return Math.round(val / 1000) + 'k'
},
},
},
dataLabels: {
enabled: false,
},
tooltip: {
x: {
format: 'dd MMM',
},
},
}
var chart = new ApexCharts(document.querySelector('#chart'), options)
chart.render()