DEV Community

Ritumoni Sarma
Ritumoni Sarma

Posted on

Understanding Canvas for High-Performance Data Grids: Why Virtualization Isn't the End of the Story

Introduction

If you've ever built a large data-intensive application, you've probably heard this advice:

"Use virtualization. It solves performance."

For many applications, that's absolutely true.

Libraries like react-window, TanStack Virtual, and react-virtualized have transformed how we render long lists by ensuring that only the visible rows exist in the DOM.

So here's the obvious question:

If virtualization already renders only 30–50 rows, why would anyone build a table using Canvas?

It's a fair question.

In fact, it's the same question I asked before diving into how products like TradingView, Figma, Excalidraw, Google Maps and VS Code are built.

The answer is surprisingly nuanced.

Canvas is not a replacement for virtualization.

Instead, it's a different rendering engine that removes an entirely different set of browser bottlenecks.

Let's understand why.


The Problem

Imagine you're building a trading dashboard.

The requirements sound simple:

  • 2 million rows
  • 150 columns
  • Real-time price updates
  • Smooth scrolling
  • Frozen columns
  • Cell selection
  • Keyboard navigation
  • Sorting
  • Filtering

Your first instinct is React.

<Table rows={rows} />
Enter fullscreen mode Exit fullscreen mode

Everything works.

Until the dataset grows.

Now scrolling stutters.

Memory usage increases.

Updates become expensive.

Even with virtualization, the UI isn't as smooth as you expected.

Why?

Because rendering data is only one part of the problem.


Understanding the Browser Rendering Pipeline

Whenever React updates the UI, the browser performs much more work than many developers realize.

React State
        │
        ▼
React Render
        │
        ▼
Virtual DOM Diff
        │
        ▼
DOM Updates
        │
        ▼
Style Calculation
        │
        ▼
Layout
        │
        ▼
Paint
        │
        ▼
Compositing
        │
        ▼

Screen
Enter fullscreen mode Exit fullscreen mode

Virtualization reduces the number of DOM nodes.

It does not eliminate the browser rendering pipeline.


Approach 1 — Render Everything

The most straightforward solution is:

<tbody>
    {rows.map(renderRow)}
</tbody>
Enter fullscreen mode Exit fullscreen mode

For 1 million rows this creates millions of DOM nodes.

The browser now has to maintain:

  • DOM tree
  • CSS tree
  • Layout tree
  • Paint tree
  • Accessibility tree

Memory usage explodes.

Scrolling becomes unusable.

This approach doesn't scale.


Approach 2 — Pagination

Another common solution is pagination.

1 2 3 4 5 Next
Enter fullscreen mode Exit fullscreen mode

Advantages:

  • Small DOM
  • Easy implementation
  • Low memory usage

Disadvantages:

  • Breaks natural scrolling
  • Poor exploration of large datasets
  • Doesn't feel like Excel

Pagination solves rendering, but sacrifices user experience.


Approach 3 — Virtualization

Virtualization changed everything.

Instead of rendering every row, we only render what's visible.

Dataset

────────────────────────

Row 0
Row 1
Row 2
...
Row 2,000,000

────────────────────────

Viewport

┌───────────────────┐
│ Row 1500          │
│ Row 1501          │
│ Row 1502          │
│ Row 1503          │
│ Row 1504          │
└───────────────────┘
Enter fullscreen mode Exit fullscreen mode

Even if the dataset contains two million rows, the browser only creates around forty DOM rows.

This is an enormous improvement.

So why isn't that enough?


The Hidden Cost of the DOM

Suppose we render only 40 visible rows.

Each row contains:

  • 10 cells
  • Icon
  • Badge
  • Tooltip
  • Status indicator

One row can easily become:

Row

├── Avatar
├── Name
├── Badge
├── Icon
├── Price
├── Status
├── Tooltip
└── Actions
Enter fullscreen mode Exit fullscreen mode

Now multiply that by 40 rows.

Hundreds or even thousands of DOM nodes still exist.

Every update still triggers:

  • Style recalculation
  • Layout
  • Paint
  • Compositing

Virtualization reduces the amount of work.

It doesn't remove the browser rendering engine.


A Different Way to Think

Canvas changes one important thing.

Instead of asking the browser to manage UI objects, we simply tell it:

Draw pixels here.

The browser only knows about one element.

<canvas></canvas>
Enter fullscreen mode Exit fullscreen mode

Everything inside the table becomes our responsibility.

There are no rows.

No cells.

No buttons.

No DOM tree.

Only pixels.


Immediate Mode vs Retained Mode

DOM uses Retained Mode Rendering.

The browser remembers every element.

DOM

Button

↓

Style

↓

Layout

↓

Paint
Enter fullscreen mode Exit fullscreen mode

Canvas uses Immediate Mode Rendering.

Draw Rectangle
↓
Draw Text
↓
Draw Border
↓
Done
Enter fullscreen mode Exit fullscreen mode

The browser doesn't remember anything.

It simply paints.


Building a Table with Canvas

A DOM table looks like this:

<td>$1250</td>
Enter fullscreen mode Exit fullscreen mode

A Canvas table looks like this:

ctx.fillRect(x, y, width, height);
ctx.fillText("$1250", x + 10, y + 18);
Enter fullscreen mode Exit fullscreen mode

Instead of creating objects, we're issuing drawing commands.


But Doesn't Canvas Redraw Everything?

Yes.

This is often viewed as a disadvantage.

Imagine 40 visible rows.

Canvas redraws all 40 rows.

At first glance this sounds expensive.

However...

Drawing 40 rectangles and 400 text labels is often significantly cheaper than asking the browser to:

  • Update DOM
  • Recalculate layout
  • Paint
  • Composite

Modern GPUs are exceptionally good at drawing pixels.


The Key Difference

Virtualization optimizes how much UI exists.

Canvas optimizes how the UI is rendered.

Those are completely different optimizations.


Comparing the Two Pipelines

React + Virtualization

User Scrolls
      │
      ▼
React
      │
      ▼
Virtual DOM
      │
      ▼
DOM Updates
      │
      ▼
Style
      │
      ▼
Layout
      │
      ▼
Paint
      │
      ▼

Screen
Enter fullscreen mode Exit fullscreen mode

Canvas Rendering

User Scrolls
      │
      ▼
Viewport
      │
      ▼
Visible Rows
      │
      ▼
Draw Commands
      │
      ▼
Canvas
      │
      ▼
GPU
      │
      ▼

Screen
Enter fullscreen mode Exit fullscreen mode

Notice what's missing.

There is no CSS layout engine.

There is no DOM update.

There is no paint tree.


Why Canvas Doesn't Compute Layout

This is a common misconception.

People often assume Canvas computes layout on every frame.

It doesn't.

The browser computes layout.

Canvas simply draws.

Suppose every column has a fixed width.

Column Width = 120

Column = 5

x = 5 × 120 = 600
Enter fullscreen mode Exit fullscreen mode

That's just multiplication.

No flexbox.

No CSS Grid.

No table layout algorithm.

No browser layout engine.


What About Data Updates?

Imagine a stock price changing.

1240

↓

1243
Enter fullscreen mode Exit fullscreen mode

Both React and Canvas must compute the new value.

The difference happens afterwards.

React:

Data

↓

React

↓

DOM

↓

Layout

↓

Paint
Enter fullscreen mode Exit fullscreen mode

Canvas:

Data
↓
fillText()
↓
Done

Enter fullscreen mode Exit fullscreen mode

Canvas doesn't eliminate business logic.

It removes browser rendering overhead.


The Next Evolution: Workers

Rendering is only part of the problem.

Large applications also need to:

  • Fetch data
  • Parse JSON
  • Sort
  • Filter
  • Search
  • Aggregate

Doing all of that on the main thread creates jank.

Instead we can move data processing into a Worker.

                 Main Thread
                     │
         User Input / Scrolling
                     │
                     ▼
               Canvas Renderer
                     ▲
                     │
             Message Channel
                     │
                     ▼
               Web Worker

          ┌─────────────────────┐
          │ Fetch Data          │
          │ Parse JSON          │
          │ Filter              │
          │ Sort                │
          │ Search              │
          │ Cache               │
          └─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Now the UI thread focuses almost entirely on interaction and rendering.


Could We Go Even Further?

Yes.

Modern browsers support OffscreenCanvas.

That means rendering itself can also move to a Worker in supported environments.

A future architecture could look like this:

Backend

     │

     ▼

Data Source

     │

     ▼

Web Worker

 ├── Fetch
 ├── Parse
 ├── Filter
 ├── Sort
 ├── Cache
 ├── Build Draw Commands

     │
     ▼
OffscreenCanvas
     │
     ▼
GPU
     │
     ▼
Display

Enter fullscreen mode Exit fullscreen mode

At this point the browser's main thread is almost entirely dedicated to user interaction.


When Should You Use Canvas?

Canvas is an excellent fit for:

  • Trading terminals
  • Financial dashboards
  • Large analytical tables
  • Whiteboards
  • Diagram editors
  • CAD tools
  • Heatmaps
  • GIS applications
  • Real-time monitoring dashboards

It's the reason products like Figma, Excalidraw, Google Maps, and TradingView can remain responsive while rendering enormous amounts of visual information.


When Should You Not Use Canvas?

Canvas introduces complexity.

If your application contains:

  • Forms
  • Inputs
  • Buttons
  • Dropdowns
  • Rich accessibility requirements
  • Small datasets

React with virtualization is usually the better choice.

Canvas isn't a universal replacement.

It's a specialized rendering engine.


Final Thoughts

Virtualization solved one of the biggest challenges on the web: too many DOM nodes.

Canvas solves a different challenge: the cost of the browser rendering engine itself.

The two approaches are not competitors.

They're complementary.

Virtualization decides what should be rendered.

Canvas decides how it should be rendered.

Understanding that distinction changes how you think about frontend architecture.

And it opens the door to building interfaces that scale to millions of rows, thousands of updates per second, and experiences that feel closer to native desktop applications than traditional web pages.

In the next article, we'll start building a production-grade Canvas-based grid engine from scratch—one subsystem at a time.

Top comments (0)