The table is the schema
Most data grids open by asking you to describe your data to them: a columns array, a rows array, a config object, all in JavaScript and all before anything appears on the screen. That is a fair contract when the page is a single-page application that had to boot a framework anyway. It is a worse fit when the page is mostly server-rendered HTML and the grid is one region inside it.
htmx is built for that second kind of page. The server owns the HTML, sends fragments back in response to requests, and htmx swaps them into place. A grid that expects to be constructed by hand, in JavaScript, inside one of those fragments is working against the model: the fragment is swapped, the grid it held is discarded, and nothing puts it back.
The markup is the schema
The grid above started as a plain <table>. Each header cell carries a
data-field that names its column, the body carries the rows, and a single call
to hydrateTable reads both and replaces the table with a live grid. There is
no columns array anywhere on the page. The shape of the data is the shape of the
table, which the server already knows how to render, so nothing is described
twice.
Two things follow from starting with a real table rather than an empty
container. The page works with JavaScript switched off: what a reader with no
scripting, or a crawler, sees is the same data in a table, not a spinner waiting
on a bundle. The grid is enhancement laid over a page that had already answered
the question. And a typed column is declared the way a server template declares
anything, with an attribute on the header cell, data-type="number", rather
than a line of client-side configuration a template engine would have to learn
to emit.
Surviving the swap
The reason to reach for the htmx module rather than a hand-written createGrid
is what happens on the next request. htmx swaps fragments of the DOM in and out,
and the module attaches to those swaps, so a grid inside a swapped region is
preserved rather than torn out and forgotten. From there it can drive sort,
filter and infinite scroll as ordinary htmx requests that the server answers,
which keeps pagination and the request lifecycle on the server where htmx
already puts everything else.
The demo on this page is the client-side half of that: a table becomes a grid, in the browser, with no server behind this page at all. The server-driven half needs a server, which a static page like this one does not have, so this shows the hydration and leaves the request-driving to the documentation.
One bundle, one attribute, one call
The integration ships as a single module that carries its own copy of the grid,
so a page loads it instead of the base build rather than beside it. The whole
setup is a data-lattice-grid attribute on the table and a call to autoInit,
and the licence, on a deployed site, travels in the same config the hydration
reads. Nothing about the page stops being HTML.