Lattice Grid Buy a licence

demo D02

One million rows in htmx

Load a million rows and scroll them, timed on your machine

rows: 1_000_000

This grid holds a million rows and lets you scroll straight through them, with the load and scroll timed on your own machine. It shows that dataset size does not change how the grid behaves, because only the rows in view are ever drawn, so a large table stays as responsive as a small one.

Building…
Loading a live grid…

This is the htmx version. A server-rendered table is upgraded in place, so the page is readable before any script runs and gains the full grid when the module loads, which suits a progressively enhanced app.

The configuration

<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.108.0/lattice-grid.min.css">

<!-- A real, server-rendered table: readable with JavaScript off. data-field on
     each header names the column that cell becomes, and data-id on each row is
     the key the grid carries it under. -->
<table id="one-million-rows-table" data-lattice-grid>
  <thead>
    <tr>
      <th data-field="account">Account</th>
      <th data-field="service">Service</th>
      <th data-field="resource">Resource</th>
      <th data-field="region">Region</th>
      <th data-field="change">Change</th>
      <th data-field="cost">Monthly cost</th>
    </tr>
  </thead>
  <tbody>
    <tr data-id="1"><td>Legacy (Do Not Decommission)</td><td>Relational Database</td><td>legacy-nat-delete-after-friday</td><td>us-east-1</td><td>-0.0468</td><td>31612.55</td></tr>
    <tr data-id="2"><td>Cost Optimisation Programme</td><td>Elastic Compute</td><td>tmp-jumpbox-03</td><td>us-east-1</td><td>0.1143</td><td>6109.09</td></tr>
    <tr data-id="3"><td>Data &amp; Insights</td><td>Block Storage</td><td>test-api-terraform-imported-by-hand</td><td>us-east-1</td><td>0.1082</td><td>42.69</td></tr>
    <!-- the rest of the rows, rendered by your server -->
  </tbody>
</table>

<script type="module">
  import { hydrateTable } from 'https://cdn.latticegrid.dev/lattice-grid@1.108.0/modules/htmx.esm.min.js';

  // The markup carries the rows; a server-rendered table's column types are
  // declared in the config hydrateTable takes. The grid replaces the table
  // where it stood.
  const grid = hydrateTable(document.getElementById('one-million-rows-table'), {
    selection: 'multiple',
    toolPanel: {
      side: 'left',
      panels: ['columns', 'filters', 'views', 'quick'],
      actions: ['undo', 'redo', 'export', 'restore', 'maximise'],
      exportName: 'cloud-costs',
    },
    columns: [
      { field: 'account', title: 'Account', filter: { type: 'set' } },
      { field: 'service', title: 'Service', filter: { type: 'set' } },
      { field: 'resource', title: 'Resource', layout: { flex: 1, min: 200, max: 320 } },
      { field: 'region', title: 'Region', filter: { type: 'set' } },
      { field: 'environment', title: 'Env', filter: { type: 'set' },
        cell: { decoration: 'pill', variant: { map: {
          prod: 'danger', staging: 'warning', test: 'info', dev: 'success',
        } } } },
      { field: 'change', title: 'Change', type: 'number',
        layout: { width: 140 }, format: { style: 'percent', decimals: 1 } },
      { field: 'cost', title: 'Monthly cost', type: 'number', total: 'sum',
        layout: { width: 170, pin: 'end' },
        format: { style: 'currency', currency: 'USD', decimals: 2 } },
    ],
  });
</script>

Virtual scrolling a JavaScript data grid to a million rows

rows: 1_000_000 sets the size of the array this demo hands to Lattice Grid, and the point of the demo is that setting it does not change how the grid behaves. Virtual scrolling means only the rows inside the visible band, plus a small overscan either side, are mounted as DOM elements; the rest stay as plain records until a scroll brings them into view. A developer reaches for this when a dataset is too large to render as one DOM row per record without stalling the browser, confirming that scroll and paint cost stay proportional to the viewport, not the row count. Because the mounted set stays constant regardless of rows length, sort, filter and keyboard navigation run against the same code path at one thousand rows as at one million; there is no separate large-data mode to opt into. Scrolling is timed on the loading machine rather than promised as a fixed number, since the figure depends on row height, column count and the hardware running it. Keyboard paging and arrow-key movement track the active cell through the virtualised set, so focus does not fall off the grid when the visible rows are replaced underneath it during a fast scroll. The demo exists to be watched, not read: load it, scroll, and see the frame cost stay flat.

How does a JavaScript data grid handle a million rows without freezing?

It renders only the rows currently visible, using virtual scrolling to mount and unmount DOM rows as the viewport moves, rather than creating one element per record up front. The rows array can hold a million entries; the grid’s paint and scroll cost tracks the number of rows on screen, typically a few dozen, not the size of the array behind them.