Chart.js: Complete Reference Guide for Data Visualization

Introduction

Chart.js is a powerful, open-source JavaScript library that allows developers to create responsive, interactive data visualizations on the web. Using HTML5 Canvas, Chart.js renders performant charts that work across all modern browsers. It’s lightweight (around 11KB when minified and gzipped), highly customizable, and provides eight chart types out of the box while supporting plugin extensions for enhanced functionality. Chart.js is ideal for projects requiring clean, professional data visualizations with minimal development effort.

Core Concepts

ConceptDescription
ChartThe main object that represents a visualization
CanvasHTML5 element that Chart.js uses for rendering
DatasetsCollections of data points to be visualized
OptionsConfiguration settings that control appearance and behavior
PluginsExtensions that add functionality to charts
ScalesAxes configurations that control how data is mapped to the visual space
ControllersComponents that define how specific chart types behave
AnimationsConfigurations that control transitions and motion effects

Setup and Installation

Using CDN

<!-- Chart.js 3.x -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<!-- Chart.js 4.x -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.3.0/dist/chart.umd.js"></script>

Using npm

# Chart.js 3.x or 4.x
npm install chart.js

# Optional dependencies for specific formats
npm install papaparse    # CSV parsing
npm install luxon        # Advanced date handling

Importing in Modern JavaScript

// ES modules
import Chart from 'chart.js/auto';
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);

// Common JS
const Chart = require('chart.js/auto');

Basic Setup

<div style="width: 500px;">
  <canvas id="myChart"></canvas>
</div>

<script>
  const ctx = document.getElementById('myChart').getContext('2d');
  const myChart = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
      datasets: [{
        label: 'My First Dataset',
        data: [12, 19, 3, 5, 2, 3],
        backgroundColor: [
          'rgba(255, 99, 132, 0.2)',
          'rgba(54, 162, 235, 0.2)',
          'rgba(255, 206, 86, 0.2)',
          'rgba(75, 192, 192, 0.2)',
          'rgba(153, 102, 255, 0.2)',
          'rgba(255, 159, 64, 0.2)'
        ],
        borderColor: [
          'rgba(255, 99, 132, 1)',
          'rgba(54, 162, 235, 1)',
          'rgba(255, 206, 86, 1)',
          'rgba(75, 192, 192, 1)',
          'rgba(153, 102, 255, 1)',
          'rgba(255, 159, 64, 1)'
        ],
        borderWidth: 1
      }]
    },
    options: {
      scales: {
        y: {
          beginAtZero: true
        }
      }
    }
  });
</script>

Chart Types

Line Chart

const lineChart = new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['January', 'February', 'March', 'April', 'May', 'June'],
    datasets: [{
      label: 'Monthly Sales',
      data: [65, 59, 80, 81, 56, 55],
      fill: false,
      borderColor: 'rgb(75, 192, 192)',
      tension: 0.1
    }]
  }
});

Bar Chart

const barChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['January', 'February', 'March', 'April', 'May', 'June'],
    datasets: [{
      label: 'Monthly Sales',
      data: [65, 59, 80, 81, 56, 55],
      backgroundColor: [
        'rgba(255, 99, 132, 0.2)',
        'rgba(54, 162, 235, 0.2)',
        'rgba(255, 206, 86, 0.2)',
        'rgba(75, 192, 192, 0.2)',
        'rgba(153, 102, 255, 0.2)',
        'rgba(255, 159, 64, 0.2)'
      ]
    }]
  }
});

Pie Chart

const pieChart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: ['Red', 'Blue', 'Yellow'],
    datasets: [{
      label: 'Dataset 1',
      data: [300, 50, 100],
      backgroundColor: [
        'rgb(255, 99, 132)',
        'rgb(54, 162, 235)',
        'rgb(255, 205, 86)'
      ]
    }]
  }
});

Doughnut Chart

const doughnutChart = new Chart(ctx, {
  type: 'doughnut',
  data: {
    labels: ['Red', 'Blue', 'Yellow'],
    datasets: [{
      label: 'Dataset 1',
      data: [300, 50, 100],
      backgroundColor: [
        'rgb(255, 99, 132)',
        'rgb(54, 162, 235)',
        'rgb(255, 205, 86)'
      ]
    }]
  }
});

Radar Chart

const radarChart = new Chart(ctx, {
  type: 'radar',
  data: {
    labels: ['Eating', 'Drinking', 'Sleeping', 'Designing', 'Coding', 'Cycling', 'Running'],
    datasets: [{
      label: 'My First Dataset',
      data: [65, 59, 90, 81, 56, 55, 40],
      fill: true,
      backgroundColor: 'rgba(255, 99, 132, 0.2)',
      borderColor: 'rgb(255, 99, 132)',
      pointBackgroundColor: 'rgb(255, 99, 132)',
      pointBorderColor: '#fff',
      pointHoverBackgroundColor: '#fff',
      pointHoverBorderColor: 'rgb(255, 99, 132)'
    }]
  }
});

Polar Area Chart

const polarAreaChart = new Chart(ctx, {
  type: 'polarArea',
  data: {
    labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple'],
    datasets: [{
      label: 'My First Dataset',
      data: [11, 16, 7, 3, 14],
      backgroundColor: [
        'rgb(255, 99, 132)',
        'rgb(75, 192, 192)',
        'rgb(255, 205, 86)',
        'rgb(54, 162, 235)',
        'rgb(153, 102, 255)'
      ]
    }]
  }
});

Scatter Chart

const scatterChart = new Chart(ctx, {
  type: 'scatter',
  data: {
    datasets: [{
      label: 'Scatter Dataset',
      data: [{x: -10, y: 0}, {x: 0, y: 10}, {x: 10, y: 5}, {x: 0.5, y: 5.5}],
      backgroundColor: 'rgb(255, 99, 132)'
    }]
  },
  options: {
    scales: {
      x: {
        type: 'linear',
        position: 'bottom'
      }
    }
  }
});

Bubble Chart

const bubbleChart = new Chart(ctx, {
  type: 'bubble',
  data: {
    datasets: [{
      label: 'First Dataset',
      data: [
        {x: 20, y: 30, r: 15},
        {x: 40, y: 10, r: 10},
        {x: 15, y: 37, r: 5},
        {x: 30, y: 25, r: 8}
      ],
      backgroundColor: 'rgb(255, 99, 132)'
    }]
  }
});

Data Structure and Configuration

Dataset Structure by Chart Type

Chart TypeData FormatSpecial Properties
Line/Bar[value1, value2, ...]tension, fill
Scatter/Bubble[{x: x1, y: y1}, {x: x2, y: y2}, ...]r (radius for bubble)
Radar[value1, value2, ...]Matches labels count
Pie/Doughnut/PolarArea[value1, value2, ...]Individual segment colors
Mixed ChartsVaries by datasetEach dataset specifies its type

Data Structure Example

const data = {
  // X-axis labels (not used in all chart types)
  labels: ['January', 'February', 'March', 'April'],
  
  // Array of dataset objects
  datasets: [
    {
      label: 'Dataset 1',
      data: [10, 20, 30, 40],
      // Visual styling
      backgroundColor: 'rgba(255, 99, 132, 0.5)',
      borderColor: 'rgb(255, 99, 132)',
      borderWidth: 1,
      // Dataset-specific options
      tension: 0.1,
      // Can include custom properties
      myCustomProperty: 'value'
    },
    {
      label: 'Dataset 2',
      data: [5, 15, 25, 35],
      backgroundColor: 'rgba(54, 162, 235, 0.5)'
    }
  ]
};

Common Options Configuration

const options = {
  // Responsive sizing
  responsive: true,
  maintainAspectRatio: false,
  
  // Chart title
  plugins: {
    title: {
      display: true,
      text: 'Chart Title',
      font: {
        size: 18
      }
    },
    // Tooltip configuration
    tooltip: {
      enabled: true,
      mode: 'index',
      intersect: false
    },
    // Legend configuration
    legend: {
      position: 'top',
      labels: {
        usePointStyle: true
      }
    }
  },
  
  // Axis configuration
  scales: {
    x: {
      title: {
        display: true,
        text: 'Month'
      },
      grid: {
        display: false
      }
    },
    y: {
      title: {
        display: true,
        text: 'Value'
      },
      beginAtZero: true,
      ticks: {
        callback: function(value) {
          return '$' + value;
        }
      }
    }
  },
  
  // Animation configuration
  animation: {
    duration: 1000,
    easing: 'easeOutQuart'
  },
  
  // Events handling
  events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove']
};

Scales and Axes Configuration

Common Scale Types

Scale TypeUsageKey Options
linearDefault for number valuesmin, max, beginAtZero
logarithmicFor exponential datamin, max
categoryFor text labelslabels
timeFor date/time datatime.unit, time.displayFormats
radialLinearFor radar chartsangleLines, pointLabels

Time Scale Configuration

scales: {
  x: {
    type: 'time',
    time: {
      unit: 'month',
      displayFormats: {
        month: 'MMM YYYY'
      },
      tooltipFormat: 'll'
    },
    title: {
      display: true,
      text: 'Date'
    }
  }
}

Logarithmic Scale

scales: {
  y: {
    type: 'logarithmic',
    min: 1,
    max: 1000
  }
}

Multiple Y Axes

scales: {
  y: {
    type: 'linear',
    display: true,
    position: 'left',
    title: {
      display: true,
      text: 'Y Axis 1'
    }
  },
  y1: {
    type: 'linear',
    display: true,
    position: 'right',
    title: {
      display: true,
      text: 'Y Axis 2'
    },
    grid: {
      drawOnChartArea: false
    }
  }
}

Customization and Styling

Colors and Styling

Single Color

backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255, 99, 132, 1)',

Color Arrays

backgroundColor: [
  'rgba(255, 99, 132, 0.2)',
  'rgba(54, 162, 235, 0.2)',
  'rgba(255, 206, 86, 0.2)'
],
borderColor: [
  'rgba(255, 99, 132, 1)',
  'rgba(54, 162, 235, 1)',
  'rgba(255, 206, 86, 1)'
],

Dynamic Colors with Functions

backgroundColor: function(context) {
  const index = context.dataIndex;
  const value = context.dataset.data[index];
  return value < 20 ? 'red' :
         value < 50 ? 'yellow' : 'green';
}

Border and Point Styling

borderWidth: 2,
borderDash: [5, 5],
borderJoinStyle: 'round',
borderCapStyle: 'round',
pointRadius: 4,
pointHoverRadius: 6,
pointBackgroundColor: 'white',
pointBorderColor: 'rgba(255, 99, 132, 1)',
pointStyle: 'circle', // 'triangle', 'rect', 'star', etc.

Fonts and Text

plugins: {
  title: {
    font: {
      family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
      size: 18,
      weight: 'bold',
      lineHeight: 1.2
    }
  },
  legend: {
    labels: {
      font: {
        family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
        size: 12
      }
    }
  }
}

Animation and Interactivity

Basic Animation

animation: {
  duration: 2000,
  easing: 'easeOutBounce',
  delay: function(context) {
    return context.dataIndex * 100;
  }
}

Animation Types

AnimationUsage
easeInOutBackSmooth with slight overshoot
easeOutQuartFast start, slow end
easeInExpoVery slow start, fast end
easeOutBounceBounces at the end
linearConstant speed

Custom Animations

const chart = new Chart(ctx, {
  // chart configuration...
});

// Animate all bars at once
chart.data.datasets[0].data = [newData];
chart.update();

// Animate specific bar
chart.data.datasets[0].data[2] = newValue;
chart.update();

// Animate with custom duration
chart.update({
  duration: 800,
  easing: 'easeOutBounce'
});

Tooltips Configuration

plugins: {
  tooltip: {
    enabled: true,
    mode: 'index', // 'point', 'nearest', 'dataset', 'x', 'y'
    intersect: false,
    position: 'nearest',
    backgroundColor: 'rgba(0, 0, 0, 0.7)',
    titleFont: {
      size: 16
    },
    bodyFont: {
      size: 14
    },
    padding: 10,
    caretSize: 5,
    displayColors: true,
    callbacks: {
      title: function(tooltipItems) {
        return 'Custom Title: ' + tooltipItems[0].label;
      },
      label: function(tooltipItem) {
        return 'Value: $' + tooltipItem.formattedValue;
      }
    }
  }
}

Events Handling

const chart = new Chart(ctx, {
  // chart configuration...
  options: {
    onClick: function(event, elements) {
      if (elements.length > 0) {
        const clickedElement = elements[0];
        console.log('Clicked on element at index:', clickedElement.index);
        console.log('Dataset:', clickedElement.datasetIndex);
        console.log('Value:', this.data.datasets[clickedElement.datasetIndex].data[clickedElement.index]);
      }
    },
    onHover: function(event, elements) {
      // Change cursor on hoverable elements
      event.native.target.style.cursor = elements.length > 0 ? 'pointer' : 'default';
    }
  }
});

// Adding listeners externally
chart.canvas.addEventListener('click', function() {
  // Custom click action
});

Responsive Behavior

Basic Responsive Options

options: {
  responsive: true,
  maintainAspectRatio: false,
  aspectRatio: 2, // only used when maintainAspectRatio is true
  
  // Specify container size when responsive is false
  width: 500,
  height: 300,
  
  // Control responsiveness breakpoints
  resizeDelay: 0 // ms to wait before resizing
}

Container Approach

<!-- Responsive container -->
<div style="position: relative; height:40vh; width:80vw">
  <canvas id="myChart"></canvas>
</div>

<!-- Fixed size container -->
<div style="position: relative; width:500px; height:300px">
  <canvas id="myChart"></canvas>
</div>

Media Queries Approach

const makeResponsive = () => {
  if (window.innerWidth < 768) {
    // Mobile configuration
    chart.options.plugins.legend.position = 'bottom';
    chart.options.scales.x.ticks.maxRotation = 90;
  } else {
    // Desktop configuration
    chart.options.plugins.legend.position = 'top';
    chart.options.scales.x.ticks.maxRotation = 0;
  }
  chart.update();
};

window.addEventListener('resize', makeResponsive);
makeResponsive(); // Initial call

Plugins and Extensions

Using Built-in Plugins

const chart = new Chart(ctx, {
  options: {
    plugins: {
      // Configure built-in plugins
      legend: { /* legend options */ },
      title: { /* title options */ },
      tooltip: { /* tooltip options */ },
      subtitle: { /* subtitle options */ },
      colors: { /* colors options */ }
    }
  }
});

Creating a Custom Plugin

const myCustomPlugin = {
  id: 'myCustomPlugin',
  beforeDraw: (chart, args, options) => {
    const {ctx, chartArea: {top, bottom, left, right, width, height}} = chart;
    ctx.save();
    
    // Draw a background
    ctx.fillStyle = options.backgroundColor || '#fcfcfc';
    ctx.fillRect(left, top, width, height);
    
    // Add watermark
    if (options.watermark) {
      ctx.globalAlpha = 0.1;
      ctx.font = '30px Arial';
      ctx.fillStyle = '#000000';
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText(options.watermark, left + width / 2, top + height / 2);
      ctx.globalAlpha = 1;
    }
    
    ctx.restore();
  }
};

// Register the plugin globally
Chart.register(myCustomPlugin);

// Or use in a specific chart
const chart = new Chart(ctx, {
  plugins: [myCustomPlugin],
  options: {
    plugins: {
      myCustomPlugin: {
        backgroundColor: '#f0f8ff',
        watermark: 'DRAFT'
      }
    }
  }
});

Popular External Plugins

PluginPurposeSource
chartjs-plugin-datalabelsDisplays data labels on your chartsgithub.com/chartjs/chartjs-plugin-datalabels
chartjs-plugin-zoomZoom and pan functionalitygithub.com/chartjs/chartjs-plugin-zoom
chartjs-plugin-annotationAdds annotations to chartsgithub.com/chartjs/chartjs-plugin-annotation
chartjs-plugin-colorschemesPredefined color schemesgithub.com/nagix/chartjs-plugin-colorschemes
chartjs-plugin-streamingReal-time streaming datagithub.com/nagix/chartjs-plugin-streaming

Advanced Techniques

Mixed Chart Types

const mixedChart = new Chart(ctx, {
  type: 'bar', // Base type
  data: {
    labels: ['January', 'February', 'March', 'April'],
    datasets: [
      {
        type: 'bar',
        label: 'Bar Dataset',
        data: [10, 20, 30, 40],
        backgroundColor: 'rgba(255, 99, 132, 0.5)'
      },
      {
        type: 'line',
        label: 'Line Dataset',
        data: [5, 15, 10, 30],
        borderColor: 'rgb(54, 162, 235)',
        fill: false
      }
    ]
  }
});

Working with Date and Time Data

// Include date adapter
import 'chartjs-adapter-date-fns';

const timeSeriesChart = new Chart(ctx, {
  type: 'line',
  data: {
    datasets: [{
      label: 'Stock Price',
      data: [
        {x: '2023-01-01', y: 100},
        {x: '2023-02-01', y: 120},
        {x: '2023-03-01', y: 110},
        {x: '2023-04-01', y: 130}
      ],
      borderColor: 'rgb(75, 192, 192)'
    }]
  },
  options: {
    scales: {
      x: {
        type: 'time',
        time: {
          unit: 'month',
          displayFormats: {
            month: 'MMM yyyy'
          }
        },
        title: {
          display: true,
          text: 'Month'
        }
      }
    }
  }
});

Custom Drawing

const chart = new Chart(ctx, {
  // chart configuration...
  plugins: [{
    id: 'customCanvasBackgroundColor',
    beforeDraw: (chart) => {
      const {ctx} = chart;
      ctx.save();
      ctx.globalCompositeOperation = 'destination-over';
      ctx.fillStyle = 'lightGreen';
      ctx.fillRect(0, 0, chart.width, chart.height);
      ctx.restore();
    }
  }]
});

Dynamic Chart Updates

// Add new data
function addData(chart, label, data) {
  chart.data.labels.push(label);
  chart.data.datasets.forEach((dataset, i) => {
    dataset.data.push(data[i]);
  });
  chart.update();
}

// Remove oldest data point
function removeData(chart) {
  chart.data.labels.shift();
  chart.data.datasets.forEach((dataset) => {
    dataset.data.shift();
  });
  chart.update();
}

// Update specific values
function updateValue(chart, datasetIndex, index, newValue) {
  chart.data.datasets[datasetIndex].data[index] = newValue;
  chart.update();
}

// Reset entire chart
function resetChart(chart) {
  chart.data.labels = [];
  chart.data.datasets.forEach((dataset) => {
    dataset.data = [];
  });
  chart.update();
}

// Live data simulation
setInterval(() => {
  // Remove oldest data point
  myChart.data.labels.shift();
  myChart.data.datasets[0].data.shift();
  
  // Add new data point
  const newLabel = new Date().toLocaleTimeString();
  const newData = Math.floor(Math.random() * 100);
  myChart.data.labels.push(newLabel);
  myChart.data.datasets[0].data.push(newData);
  
  // Update with animation
  myChart.update();
}, 1000);

Common Challenges and Solutions

ChallengeSolution
Chart doesn’t displayCheck if canvas element exists before Chart instantiation; verify data format is correct
Chart not responsiveSet responsive: true and ensure parent container has dimensions
Tooltips not showingCheck plugins.tooltip.enabled is true; ensure data points are valid
Y-axis doesn’t start at zeroSet scales.y.beginAtZero: true
Legend position wrongConfigure with plugins.legend.position: 'top'/'left'/'right'/'bottom'
Chart animation too fast/slowAdjust animation.duration (milliseconds)
Data labels overlappingUse datalabels plugin with proper padding, rotation or display conditions
Inconsistent colorsDefine consistent color palette in datasets
Line chart straight linesAdjust tension property (0 = straight, 0.4 = curved)
Chart performance issuesLimit number of data points; disable animations for large datasets

Best Practices

Performance

  • Limit number of data points displayed at once (use aggregation for large datasets)
  • Disable animations for charts with more than 1000 data points
  • Use simple tooltips for large datasets
  • Consider disabling hover interactions for performance-critical applications
  • Use requestAnimationFrame for smooth animations when updating dynamically

Accessibility

  • Include proper alt text for canvas elements
  • Use accessible color schemes with sufficient contrast
  • Add aria attributes to the container
  • Consider including a data table alternative
  • Test with screen readers

Code Organization

  • Separate chart configurations into their own files/functions
  • Create reusable utility functions for common chart tasks
  • Use consistent color schemes across your application
  • Document custom plugins and configurations
  • Implement proper error handling for data loading

Resources for Further Learning

Official Documentation

Additional Resources

Community

Scroll to Top