DEV Community

Øystein Amundsen
Øystein Amundsen

Posted on

I built a data grid with web components – here's what I learned

A while back I needed a data grid for a project. You know the drill – looked at AG Grid (too heavy, enterprise pricing), TanStack Table (great but headless, didn't want to build all the UI), various React/Vue-specific options (locked into one framework).

I was also working across multiple projects – some Angular, some React, some just vanilla JS. The idea of maintaining different grid implementations for each was... not appealing.

So I did what any reasonable developer does: I spent way more time building my own thing than I would have spent just using an existing solution. 😅

The idea

Web components have been "almost ready" for years, but they're actually pretty solid now. Custom elements, CSS nesting, adoptedStyleSheets – the browser APIs are there.

So I built @toolbox-web/grid:

<tbw-grid></tbw-grid>

<script type="module">
  import '@toolbox-web/grid';

  const grid = document.querySelector('tbw-grid');
  grid.columns = [
    { field: 'name', header: 'Name' },
    { field: 'email', header: 'Email' },
  ];
  grid.rows = data;
</script>
Enter fullscreen mode Exit fullscreen mode

No framework. Just a custom element. Works in React, Angular, Vue, or plain HTML.

What's in it

The basics you'd expect:

  • Sorting, filtering, inline editing
  • Column resizing, reordering, pinning
  • Row selection (single, multi, range)
  • Row virtualization (handles 100k+ rows)

And some stuff I needed for specific projects:

  • Row grouping and tree data
  • Master-detail panels
  • Clipboard support (copy/paste)
  • Undo/redo for edits
  • CSV/Excel export

Everything is a plugin, so you only ship what you use. The core is pretty small (~40KB gzipped with common plugins).

Things I learned building this

Shadow DOM is a pain

I started with Shadow DOM for style encapsulation. Seemed like the "right" way. But:

  • Users couldn't style the grid with their existing CSS
  • Server-side rendering was a mess
  • DevTools debugging was awkward

Switched to light DOM with CSS nesting. You get scoping without the isolation headaches.

One render loop to rule them all

Early versions had render calls scattered everywhere. Change a property? Render. Resize a column? Render. Sort? Render. This caused layout thrashing and flickering.

Now everything goes through a single scheduler:

state change → queue → batch → one RAF → render
Enter fullscreen mode Exit fullscreen mode

Doesn't matter how many things change in a frame, there's one render pass.

Plugins are just classes

I went back and forth on the plugin architecture. Ended up with simple classes:

class MyPlugin extends BaseGridPlugin<MyConfig> {
  readonly name = 'myPlugin';

  processRows(rows) {
    // transform data before render
    return rows;
  }

  afterRender() {
    // do stuff with the DOM
  }
}
Enter fullscreen mode Exit fullscreen mode

Constructor injection for config, lifecycle hooks for different phases. Nothing fancy, but it works well.

Framework adapters

The raw custom element works everywhere, but framework-specific wrappers make life nicer.

React:

import { DataGrid } from '@toolbox-web/grid-react';

<DataGrid
  rows={employees}
  gridConfig={{
    columns: [
      { field: 'name' },
      { field: 'status', renderer: (ctx) => <Badge value={ctx.value} /> },
    ],
  }}
/>
Enter fullscreen mode Exit fullscreen mode

Angular:

<tbw-grid [rows]="data" [gridConfig]="config">
  <tbw-grid-column field="status">
    <my-badge *tbwRenderer="let value" [status]="value" />
  </tbw-grid-column>
</tbw-grid>
Enter fullscreen mode Exit fullscreen mode

You get proper types, native component renderers, and framework-idiomatic APIs.

Rough comparison

Just for context – not trying to say mine is "better", they're different tools:

@toolbox-web/grid AG Grid TanStack Table
Bundle ~40KB ~500KB ~15KB (headless)
UI included Yes Yes No
Framework Any Wrappers React/Vue/Solid
License MIT MIT + paid MIT

AG Grid is way more mature and feature-complete. TanStack is brilliant if you want full control over rendering. This sits somewhere in between – batteries included but not as heavy.

Try it out

It's MIT licensed, free, no tracking, no enterprise upsell. Just a thing I built that might be useful to others.

npm install @toolbox-web/grid
Enter fullscreen mode Exit fullscreen mode

Would love feedback, bug reports, or feature ideas. I'm actively working on it and trying to make it as useful as possible.


What data grid do you use? Curious what features people actually care about.

Top comments (0)