During my 20+ years as a developer, I've noticed that there is one thing I've always needed to have in all my applications; a data-grid. So before I start work on any application I have to consider what grid support I need.

Almost all of the time this grid support is a major player in my application and I'm forced to take a stand at how much I'm willing to spend on this support. This is primarily why I at a point decided; how hard can it be to code a thing that does what I want my self?

The answer was @toolbox-web/grid: https://toolboxjs.com

What is it?

This began as a component library based on pure web components. I currently only have one component — the grid — but it's a capable one. It's covered by adapters for Angular, React, and Vue, and can also be run standalone with vanilla JS if you prefer.

The grid renders in the Light DOM (no Shadow DOM), so your existing CSS cascade works normally. It uses em-based sizing, meaning you can scale the entire grid just by changing the font-size on the host element.

Getting started

Install the core package and your framework adapter:

npm install @toolbox-web/grid @toolbox-web/grid-angular

Then set up a basic grid in your Angular component:

import { Grid } from '@toolbox-web/grid-angular';
import type { ColumnConfig } from '@toolbox-web/grid';

@Component({
  imports: [Grid],
  template: `
    <tbw-grid
      [rows]="data()"
      [columns]="columns"
      style="height: 400px; display: block;"
    />
  `,
})
export class MyComponent {
  data = signal<Employee[]>([]);

  columns: ColumnConfig<Employee>[] = [
    { field: 'name', header: 'Name' },
    { field: 'role', header: 'Role' },
    { field: 'department', header: 'Department' },
  ];
}

Important: tbw-grid must have an explicit height and display: block set — without these the grid won't render.

Core features

These are available out of the box, no extra imports needed:

  • Cell formatters & custom renderers
  • Row & cell styling
  • Custom header renderers
  • Column resizing
  • Column state persistence
  • Loading states
  • Row/Column animations

For example, you can add a custom renderer and a date formatter to your columns:

const columns: ColumnConfig<Employee>[] = [
  { field: 'name', header: 'Name', sortable: true },
  {
    field: 'status',
    header: 'Status',
    renderer: (ctx) => {
      const span = document.createElement('span');
      span.className = `badge badge-${ctx.value}`;
      span.textContent = ctx.value;
      return span;
    },
  },
  {
    field: 'startDate',
    header: 'Started',
    type: 'date',
    format: (value) =>
      value ? new Date(value).toLocaleDateString() : '',
  },
];

Plugin architecture

Every advanced feature is a plugin that you opt into with a side-effect import. This keeps your bundle lean — you only ship what you use.

// Only import the features you need
import '@toolbox-web/grid-angular/features/selection';
import '@toolbox-web/grid-angular/features/filtering';
import '@toolbox-web/grid-angular/features/editing';

Then activate them on the grid element:

<tbw-grid
  [rows]="data()"
  [columns]="columns"
  [selection]="'row'"
  [filtering]="true"
  [editing]="'dblclick'"
  (cellCommit)="onCellCommit($event)"
  style="height: 400px; display: block;"
/>

The full list of available plugins:

  • Selection — row, cell, or range selection with optional checkboxes
  • Filtering — built-in filter panels for primitive types, or bring your own
  • Editing — inline cell editing with built-in and custom editors
  • Multi Sort — sort by multiple columns simultaneously
  • Virtualization — row and column virtualization that handles millions of rows without breaking a sweat
  • Export — CSV, Excel, and JSON export
  • Clipboard — copy/paste support (requires Selection)
  • Undo/Redo — revert edits (requires Editing)
  • Context Menu — right-click menus
  • Column/Row Grouping — group rows by field values
  • Pinned Columns/Rows — freeze columns or rows in place
  • Master Detail — expandable row details
  • Pivot — pivot table transformation
  • Drag & Drop — reorder columns and rows
  • Responsive — automatic card layout below a breakpoint
  • Server Side — lazy loading with infinite scroll, paging, remote sort/filter
  • Tree View — hierarchical row display

Theming

The grid uses CSS custom properties, making it trivial to match your design system:

tbw-grid {
  --tbw-color-bg: transparent;
  --tbw-color-fg: #333;
  --tbw-color-accent: #1976d2;
  --tbw-color-border: #e0e0e0;
  --tbw-color-header-bg: #f5f5f5;
  --tbw-color-row-hover: #f0f7ff;
  --tbw-color-selection: #e3f2fd;
  --tbw-row-height: 1.75em;
  --tbw-header-height: 1.875em;
}

There are also several built-in themes you can apply as CSS classes: dg-theme-material, dg-theme-bootstrap, dg-theme-vibrant, dg-theme-contrast, and more.

Angular template renderers

For more complex cells, you can use Angular template directives instead of vanilla JS renderers:

<tbw-grid [rows]="employees()" [columns]="columns" [editing]="'dblclick'">
  <tbw-grid-column field="status" header="Status" [editable]="true">
    <span *tbwRenderer="let value; row as row" [class]="'badge badge-' + value">
      {{ value }}
    </span>
    <select *tbwEditor="let value; onCommit as onCommit"
            (change)="onCommit($event.target.value)">
      <option value="active" [selected]="value === 'active'">Active</option>
      <option value="inactive" [selected]="value === 'inactive'">Inactive</option>
    </select>
  </tbw-grid-column>
</tbw-grid>

This gives you the full power of Angular's template syntax inside each cell — data binding, pipes, event handlers, you name it.

What's next?

I'm actively developing the library and there's always more to build. If you're curious, check out the docs at toolboxjs.com or grab the package from npm. Feedback and contributions are always welcome.