“I Tried Making Beautiful JavaScript Charts. Here’s What Actually Worked.”

I make dashboards. Sales, traffic, signups, the usual mess. I care about looks, but I also care about time. Can I ship fast? Will it run smooth on a phone? Will my boss say “wow” and not “ugh”?

So I tried a bunch of chart tools. I used them on real work. I broke some things. I fixed them. And yeah, I have feelings about it. (I documented the whole experiment in a no-fluff recap you can skim here.)

You know what? Pretty charts are nice. But clear charts feel even better. If you just want the easiest path to sane defaults, there's a quick checklist over here.


My quick wish list

  • Clean by default
  • Fast on mobile
  • Dark mode that doesn’t look muddy
  • Good tooltips (no tiny text)
  • Exports that don’t blur when printed
  • A path for big data, like 50k points
  • Basic a11y (labels, focus, ARIA)

If you want a hosted, out-of-the-box option that already nails most of those bullets, give EJSchart a whirl. For a side-by-side look at seven popular libraries, check out this comparison.

That’s not a small ask. Still, a few tools got me close.


Chart.js — my easy go-to

I used Chart.js for a coffee shop dashboard. It showed daily revenue and average order size. I had two days to make it. Chart.js was like a buddy. It just worked.

What I like:

  • Good defaults and quick wins
  • Plugins for labels and notes
  • The “tension” line looks smooth
  • Built-in decimation helped with large sets

What bugged me:

  • Big bundle if you add lots of plugins
  • Past 50k points, it can drag

Here’s a small line chart I shipped. Pretty, soft, and readable.

<canvas id="revenue" height="160"></canvas>
<script>
const ctx = document.getElementById('revenue').getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, 0, 200);
gradient.addColorStop(0, 'rgba(99,102,241,0.6)');
gradient.addColorStop(1, 'rgba(99,102,241,0.05)');

new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Jan','Feb','Mar','Apr','May','Jun'],
    datasets: [{
      data: [12,19,9,17,23,28],
      borderColor: '#6366f1',
      backgroundColor: gradient,
      fill: true,
      tension: 0.35,
      pointRadius: 2
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: { display: false },
      tooltip: { callbacks: { label: c => `$${c.parsed.y}k` } },
      decimation: { enabled: true, algorithm: 'lttb' }
    },
    scales: {
      y: { ticks: { callback: v => `$${v}k` } }
    }
  }
});
</script>

A tiny note: use system fonts and enough white space. It breathes better. Need more inspiration for gorgeous yet lightweight visuals? This walkthrough lays out the small tweaks that make charts pop.


ECharts — big, bold, and kind of fun

For a school site, I needed traffic charts with pan and zoom. Parents loved to pinch and zoom on phones. Apache ECharts felt flashy in a good way. The zoom tool and export button were a hit. For the scoop on how the biggest chart tool names stack up, see this field report.

Good stuff:

  • Themes look rich
  • Zoom and pan feel smooth
  • Exports are crisp
  • Huge data didn’t choke

Tough stuff:

  • The API names are… a lot
  • You’ll read the docs more than you want

Real code I used (trimmed):

<div id="traffic" style="height:280px;"></div>
<script>
var chart = echarts.init(document.getElementById('traffic'));
chart.setOption({
  tooltip: { trigger: 'axis' },
  toolbox: { feature: { saveAsImage: {} } },
  dataZoom: [{ type: 'inside' }, { }],
  xAxis: { type: 'category', boundaryGap: false, data: datesArray },
  yAxis: { type: 'value' },
  series: [{
    name: 'Visits',
    type: 'line',
    smooth: true,
    areaStyle: {},
    emphasis: { focus: 'series' },
    data: visitsArray
  }]
});
</script>

I used the “dark” theme one night. It looked like a city at midnight. Kind of loved it.


D3 — total control, but bring snacks

I made a custom survey chart for a non-profit gala. They wanted a “lollipop” chart with cute dots. Off the shelf tools couldn’t do it. So, D3.

Why D3?

  • You control every pixel
  • Great scales and axes
  • Can be small if you pick parts

But:

  • You write more code
  • Data joins take a minute to learn

My tiny lollipop sample:

<svg id="lolli" viewBox="0 0 420 200"></svg>
<script>
const data = [
  { label:'Food', value:82 },
  { label:'Music', value:67 },
  { label:'Decor', value:41 },
  { label:'Venue', value:74 }
];

const svg = d3.select('#lolli');
const x = d3.scaleLinear().domain([0,100]).range([100,380]);
const y = d3.scaleBand().domain(data.map(d=>d.label)).range([20,180]).padding(0.4);

svg.append('g').attr('transform','translate(0,0)')
  .selectAll('line').data(data).join('line')
  .attr('x1',100).attr('x2',d=>x(d.value))
  .attr('y1',d=>y(d.label)+y.bandwidth()/2)
  .attr('y2',d=>y(d.label)+y.bandwidth()/2)
  .attr('stroke','#cbd5e1').attr('stroke-width',4);

svg.append('g').selectAll('circle').data(data).join('circle')
  .attr('cx',d=>x(d.value))
  .attr('cy',d=>y(d.label)+y.bandwidth()/2)
  .attr('r',8).attr('fill','#10b981');

svg.append('g').attr('transform','translate(0,0)').call(d3.axisLeft(y));
svg.append('g').attr('transform','translate(0,180)').call(d3.axisBottom(x).ticks(5).tickFormat(d=>d+'%'));
</script>

It felt hand-made, which was the point. If you're hunting for open-source options beyond the usual suspects, this write-up might help: I tried a bunch of open-source JavaScript chart tools—here's what actually worked.


Recharts (React) — smooth in React land

My team ships React. Recharts fits that mood. It uses components, so state and props feel natural. Not the flashiest, but it’s calm and steady.

Pros:

  • Great DX in React
  • Composable parts
  • Easy theming with CSS

Cons:

  • Heavy sets can lag
  • Less wild chart types

Tiny example:

<LineChart width={320} height={160} data={data}>
  <XAxis dataKey="day" />
  <YAxis />
  <Tooltip />
  <Legend />
  <Line type="monotone" dataKey="orders" stroke="#f59e0b" dot={false} />
</LineChart>

I used this in a sales card list. It felt snappy.


Highcharts — polished, but watch the license

For a finance client, I used Highcharts Stock. The range selector and flags saved me hours. It looks fancy without much work. But heads up: you need a license for paid work.

What I saw:

  • Great time series tools
  • Crisp labels and exports
  • A11y module is solid

Trade-off:

  • Cost, of course

Want to stay in the free tier? There’s a rundown of no-cost libraries right here.


Plotly.js — science class energy

I used Plotly for