V

Vuer-Viz

vuer-vizv1.0.2

Publication-Quality Visualizations

Rendering A Million Points with <canvas/>

Table of Contents

Use canvas={true} for large datasets (1000+ points). Canvas renders data 20-30x faster than SVG, while axes and labels remain crisp in SVG.

LineChart with Canvas

Basic Usage

0m1m00.20.40.60.81
Data

Option + Scroll to zoom, Click + Drag to pan. Viewing 0 - 1000 (0.1% of 1M points).

import { useState } from "react";
import { durationMinutes, LineChart, SeriesTooltip, useZoomPan, VerticalMarker } from "@vuer-ai/vuer-viz";
import { generateSineWaveData } from "./generateData";

const { x, y } = generateSineWaveData(1_000_000);
const series = [{ label: "Data", x, y, color: "#3498db" }];

export default function BasicCanvas() {
  const [cursorX, setCursorX] = useState<number | null>(null);
  const [xMin, setXMin] = useState(0);
  const [xMax, setXMax] = useState(1_000);

  const { plotRef } = useZoomPan({min : xMin, max : xMax, setMin : setXMin, setMax : setXMax, minLimit : 0, maxLimit : 1_000_000});

  return (
    <div>
      <LineChart
        plotRef={plotRef}
        series={series}
        width={770}
        canvas
        xMin={xMin}
        xMax={xMax}
        xFormat="minutes"
        onChange={(e) => setCursorX(e.x)}
      >
        <VerticalMarker x={cursorX} />
        <SeriesTooltip x={cursorX} series={series} xFormat="minutes"/>
      </LineChart>
      <p className="mt-2 text-sm text-gray-600 dark:text-gray-400">
        Option + Scroll to zoom, Click + Drag to pan.
        Viewing {xMin.toFixed(0)} - {xMax.toFixed(0)} ({((xMax - xMin) / 10000).toFixed(1)}% of 1M points).
      </p>
    </div>
  );
}

Multi-Series with Canvas

Canvas rendering supports all line chart features:

00.20.40.60.8100.20.40.60.81
Series A
Series B
import { LineChart } from '@vuer-ai/vuer-viz';
import { generateMultiSineData } from './generateData';

const [dataA, dataB] = generateMultiSineData(5000, 2);

export default function MultiSeriesCanvas() {
  return (
    <LineChart
      series={[
        {
          label: 'Series A',
          x: dataA.x,
          y: dataA.y,
          color: '#23aaff',
          lineWidth: 2,
          showPoints: false,
        },
        {
          label: 'Series B',
          x: dataB.x,
          y: dataB.y,
          color: '#ffaf24',
          lineWidth: 2,
          dashArray: '5 5',
          showPoints: true,
          pointRadius: 3,
        },
      ]}
      canvas
      width={770}
      height={400}
    />
  );
}

Performance Comparison

Dataset SizeSVG Render TimeCanvas Render TimeImprovement
100 points~5ms~3ms1.7x faster
1,000 points~50ms~8ms6x faster
10,000 points~500ms~25ms20x faster
50,000 points~2500ms (slow)~80ms31x faster

Times measured on a modern laptop. Results may vary based on hardware.

Live Comparison: SVG vs Canvas

Below are two identical charts with 5,000 data points. The first uses SVG rendering (default), the second uses canvas rendering. Notice the difference in rendering performance.

SVG Rendering (5,000 points)

SVG Rendering (5,000 points)00.20.40.60.8100.20.40.60.81X AxisY Axis
SVG Rendering
import { LineChart } from '@vuer-ai/vuer-viz';
import { generateSineWaveData } from './generateData';

// Generate 5,000 data points
const { x, y } = generateSineWaveData(5000);

export default function SvgLargeDataset() {
  return (
    <LineChart
      series={[
        {
          label: 'SVG Rendering',
          x,
          y,
          color: '#23aaff',
          lineWidth: 2,
          showPoints: false,
        },
      ]}
      title="SVG Rendering (5,000 points)"
      xLabel="X Axis"
      yLabel="Y Axis"
      width={770}
      height={400}
      grid={true}
      legend={true}
    />
  );
}

Canvas Rendering (5,000 points)

Canvas Rendering (5,000 points)00.20.40.60.8100.20.40.60.81X AxisY Axis
Canvas Rendering
import { LineChart } from '@vuer-ai/vuer-viz';
import { generateSineWaveData } from './generateData';

// Generate 5,000 data points (same as SVG example)
const { x, y } = generateSineWaveData(5000);

export default function CanvasLargeDataset() {
  return (
    <LineChart
      series={[
        {
          label: 'Canvas Rendering',
          x,
          y,
          color: '#ffaf24',
          lineWidth: 2,
          showPoints: false,
        },
      ]}
      title="Canvas Rendering (5,000 points)"
      xLabel="X Axis"
      yLabel="Y Axis"
      width={770}
      height={400}
      grid={true}
      legend={true}
      canvas={true}
    />
  );
}

The canvas version renders significantly faster, especially on initial load and when resizing the browser window.

BarChart with Canvas

Vertical Bars

00.20.40.60.81Item 0Item 1Item 2Item 3Item 4Item 5Item 6Item 7Item 8Item 9Item 10Item 11Item 12Item 13Item 14Item 15Item 16Item 17Item 18Item 19Item 20Item 21Item 22Item 23Item 24Item 25Item 26Item 27Item 28Item 29Item 30Item 31Item 32Item 33Item 34Item 35Item 36Item 37Item 38Item 39Item 40Item 41Item 42Item 43Item 44Item 45Item 46Item 47Item 48Item 49Item 50Item 51Item 52Item 53Item 54Item 55Item 56Item 57Item 58Item 59Item 60Item 61Item 62Item 63Item 64Item 65Item 66Item 67Item 68Item 69Item 70Item 71Item 72Item 73Item 74Item 75Item 76Item 77Item 78Item 79Item 80Item 81Item 82Item 83Item 84Item 85Item 86Item 87Item 88Item 89Item 90Item 91Item 92Item 93Item 94Item 95Item 96Item 97Item 98Item 99Item 100Item 101Item 102Item 103Item 104Item 105Item 106Item 107Item 108Item 109Item 110Item 111Item 112Item 113Item 114Item 115Item 116Item 117Item 118Item 119Item 120Item 121Item 122Item 123Item 124Item 125Item 126Item 127Item 128Item 129Item 130Item 131Item 132Item 133Item 134Item 135Item 136Item 137Item 138Item 139Item 140Item 141Item 142Item 143Item 144Item 145Item 146Item 147Item 148Item 149Item 150Item 151Item 152Item 153Item 154Item 155Item 156Item 157Item 158Item 159Item 160Item 161Item 162Item 163Item 164Item 165Item 166Item 167Item 168Item 169Item 170Item 171Item 172Item 173Item 174Item 175Item 176Item 177Item 178Item 179Item 180Item 181Item 182Item 183Item 184Item 185Item 186Item 187Item 188Item 189Item 190Item 191Item 192Item 193Item 194Item 195Item 196Item 197Item 198Item 199
import { BarChart } from '@vuer-ai/vuer-viz';

const label = Array.from({ length: 200 }, (_, i) => `Item ${i}`);
const value = Array.from({ length: 200 }, () => Math.random() * 100);

export default function BarCanvas() {
  return (
    <BarChart
      label={label}
      value={value}
      canvas
      orientation="vertical"
    />
  );
}

Horizontal Bars

Canvas works with both orientations:

<BarChart
  data={data}
  canvas
  orientation="horizontal"
  showLabels={true}  // Value labels also render on canvas
/>

Colored Bars

Individual bar colors work seamlessly:

const coloredData = [
  { label: 'A', value: 100, color: '#23aaff' },
  { label: 'B', value: 85, color: '#ffaf24' },
  { label: 'C', value: 92, color: '#ff4be1' },
  // ... 100+ more bars
];

<BarChart
  data={coloredData}
  canvas
/>

Canvas Features

High-DPI Display Support

Canvas rendering automatically accounts for device pixel ratio:

// Automatically crisp on retina displays
<LineChart
  series={data}
  canvas
/>

The canvas is internally scaled to match your display's pixel density, ensuring sharp rendering on all devices.

Value Labels on Canvas

When using showLabels={true}, labels are rendered directly on the canvas:

<BarChart
  data={data}
  canvas
  showLabels={true}
  fontSize={14}
/>

Dash Arrays

Dashed lines work on canvas using the same syntax:

<LineChart
  series={[
    {
      label: 'Dashed',
      data: data,
      dashArray: '5 5',  // 5px dash, 5px gap
    },
  ]}
  canvas
/>

Best Practices

1. Profile Before Optimizing

Start with SVG (default). Only switch to canvas if you experience performance issues.

// Start here
<LineChart series={data} />

// If slow, add canvas
<LineChart series={data} canvas />

2. Use React.memo

Prevent unnecessary re-renders:

const MemoizedChart = React.memo(LineChart);

<MemoizedChart
  series={data}
  canvas
/>

3. Memoize Data

Avoid recreating datasets on every render:

const data = useMemo(() => {
  return generateLargeDataset();
}, [dependencies]);

<LineChart series={data} canvas />

4. Throttle Updates

For real-time charts, throttle updates:

import { useEffect, useState } from 'react';
import { throttle } from 'lodash';

function RealtimeChart() {
  const [data, setData] = useState(initialData);

  useEffect(() => {
    const updateData = throttle((newData) => {
      setData(newData);
    }, 100); // Update at most every 100ms

    const subscription = dataStream.subscribe(updateData);
    return () => subscription.unsubscribe();
  }, []);

  return <LineChart series={data} canvas />;
}

Switching Between Modes

You can dynamically toggle between canvas and SVG:

function AdaptiveChart({ data }) {
  const canvas = data[0].data.length > 1000;

  return (
    <LineChart
      series={data}
      canvas={canvas}
    />
  );
}

The canvas is embedded in SVG using <foreignObject>, allowing seamless integration with SVG UI elements.

Canvas Context

Charts use 2D canvas context with these settings:

  • Device pixel ratio scaling for high-DPI displays
  • Anti-aliasing enabled for smooth lines
  • Line join: round (for smooth corners)
  • Line cap: round (for smooth endpoints)

Conclusion

Canvas rendering in Vuer-Viz provides:

  • 20-30x faster rendering for large datasets
  • 📱 Better mobile performance
  • 🎨 Hybrid approach keeps text crisp
  • 🔧 Easy toggle with single prop

Use it when performance matters, stick with SVG when interactivity and accessibility are priorities.