Skip to content

How to Draw a Linear Chart with Canvas on React

In this article, we will learn how to draw a linear chart using the HTML Canvas element in React.

Given the following coordinates of points. We are required to draw a linear chart using the HTML Canvas element.

const data = [
  { x: 0, y: 50 },
  { x: 1, y: 80 },
  { x: 2, y: 20 },
  { x: 3, y: 90 },
  { x: 4, y: 40 },
  { x: 5, y: 60 },
  { x: 6, y: 75 },
];

Draw Axes

First, we need to draw the axises. We will draw the x-axis and y-axis.

// Draw axes
ctx.beginPath();
ctx.strokeStyle = "#333";
ctx.lineWidth = 1;

// Y-axis
ctx.moveTo(padding, padding);
ctx.lineTo(padding, height - padding);

// X-axis
ctx.moveTo(padding, height - padding);
ctx.lineTo(width - padding, height - padding);
ctx.stroke();

Draw Grid Lines and Labels

// Draw grid lines and labels
ctx.strokeStyle = "#eee";
ctx.fillStyle = "#666";
ctx.textAlign = "right";
ctx.textBaseline = "middle";

// Y-axis grid and labels
for (let i = 0; i <= 10; i++) {
  const y = height - padding - (i * chartHeight) / 10;
  ctx.beginPath();
  ctx.moveTo(padding, y);
  ctx.lineTo(width - padding, y);
  ctx.stroke();

  ctx.fillText(`${i * 10}`, padding - 5, y);
}

// X-axis labels
ctx.textAlign = "center";
ctx.textBaseline = "top";
data.forEach((point, index) => {
  const x = padding + index * xScale;
  ctx.fillText(point.x.toString(), x, height - padding + 5);
});

Draw Line

ctx.beginPath();
ctx.strokeStyle = "#2563eb";
ctx.lineWidth = 2;

data.forEach((point, index) => {
  const x = padding + index * xScale;
  const y = height - padding - point.y * yScale;

  if (index === 0) {
    ctx.moveTo(x, y);
  } else {
    ctx.lineTo(x, y);
  }
});
ctx.stroke();

Render points

// Draw points
data.forEach((point, index) => {
  const x = padding + index * xScale;
  const y = height - padding - point.y * yScale;

  ctx.beginPath();
  ctx.fillStyle = "#2563eb";
  ctx.arc(x, y, 4, 0, Math.PI * 2);
  ctx.fill();
});

Full Code

import React, { useEffect, useRef } from 'react';

const CanvasLineChart = () => {
  const canvasRef = useRef(null);
  
  // Sample data
  const data = [
    { x: 0, y: 50 },
    { x: 1, y: 80 },
    { x: 2, y: 20 },
    { x: 3, y: 90 },
    { x: 4, y: 40 },
    { x: 5, y: 60 },
    { x: 6, y: 75 }
  ];

  const drawChart = (ctx, width, height) => {
    // Clear canvas
    ctx.clearRect(0, 0, width, height);
    
    // Chart dimensions
    const padding = 40;
    const chartWidth = width - padding * 2;
    const chartHeight = height - padding * 2;
    
    // Scale factors
    const xScale = chartWidth / (data.length - 1);
    const yScale = chartHeight / 100;  // Assuming data ranges from 0-100
    
    // Draw axes
    ctx.beginPath();
    ctx.strokeStyle = '#333';
    ctx.lineWidth = 1;
    
    // Y-axis
    ctx.moveTo(padding, padding);
    ctx.lineTo(padding, height - padding);
    
    // X-axis
    ctx.moveTo(padding, height - padding);
    ctx.lineTo(width - padding, height - padding);
    ctx.stroke();
    
    // Draw grid lines and labels
    ctx.strokeStyle = '#eee';
    ctx.fillStyle = '#666';
    ctx.textAlign = 'right';
    ctx.textBaseline = 'middle';
    
    // Y-axis grid and labels
    for (let i = 0; i <= 10; i++) {
      const y = height - padding - (i * chartHeight / 10);
      ctx.beginPath();
      ctx.moveTo(padding, y);
      ctx.lineTo(width - padding, y);
      ctx.stroke();
      
      ctx.fillText(`${i * 10}`, padding - 5, y);
    }
    
    // X-axis labels
    ctx.textAlign = 'center';
    ctx.textBaseline = 'top';
    data.forEach((point, index) => {
      const x = padding + index * xScale;
      ctx.fillText(point.x.toString(), x, height - padding + 5);
    });
    
    // Draw line
    ctx.beginPath();
    ctx.strokeStyle = '#2563eb';
    ctx.lineWidth = 2;
    
    data.forEach((point, index) => {
      const x = padding + index * xScale;
      const y = height - padding - (point.y * yScale);
      
      if (index === 0) {
        ctx.moveTo(x, y);
      } else {
        ctx.lineTo(x, y);
      }
    });
    ctx.stroke();
    
    // Draw points
    data.forEach((point, index) => {
      const x = padding + index * xScale;
      const y = height - padding - (point.y * yScale);
      
      ctx.beginPath();
      ctx.fillStyle = '#2563eb';
      ctx.arc(x, y, 4, 0, Math.PI * 2);
      ctx.fill();
    });
  };
  
  useEffect(() => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    
    // Set canvas size
    canvas.width = 600;
    canvas.height = 400;
    
    // Handle high DPI displays
    const dpr = window.devicePixelRatio || 1;
    const rect = canvas.getBoundingClientRect();
    
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    
    ctx.scale(dpr, dpr);
    canvas.style.width = `${rect.width}px`;
    canvas.style.height = `${rect.height}px`;
    
    drawChart(ctx, rect.width, rect.height);
  }, []);
  
  return (
    <div className="p-4">
      <canvas
        ref={canvasRef}
        className="w-full max-w-2xl border border-gray-200 rounded-lg"
      />
    </div>
  );
};

export default CanvasLineChart;

Demo