Lattice Grid Buy a licence

developer guide

Connect your data

One source config block, and switching strategy is a one-line change. Hold it all in memory, page it, push the work to the server, stream it, or derive it from another grid.

The source is where the data comes from

A source is one block under a single source key, so switching strategy is a one-line change and the rest of your configuration is untouched. It is also the one decision that decides whether the grid stays fast on your data: a million rows can sit in the browser and be sorted, filtered, grouped and totalled locally, but past that, or when the data lives behind an API or a warehouse, the source is what moves the work to where the data already is.

// Every source is one config block. Switching strategy is a one-line change.
createGrid(el, { columns, rowKey: 'id', source: { mode: 'remote', pageSize: 100, fetch } });

There are five source modes, and a set of adapters that sit in front of the remote mode to speak a specific back end for you. This page is the map; each option has its own page with the full setup.

The five modes

Every mode satisfies the same source interface, so the grid on top of it behaves identically. What changes is where the rows come from and who does the sorting, filtering and grouping.

ModeForWhere the work runs
memoryEverything present, up to about a million rows. The default.The browser does all of it.
pagedA page at a time from a server that paginates.The server, one page per request.
remoteDatasets too large to hold, fetched in blocks as the user scrolls.The server: sort, filter, grouping, totals and pivot.
streamRows arriving over time from a query, a socket or a generator.The browser, as rows land, then promotes to memory.
derivedA grid computed from another grid.The browser, re-derived when the source moves.

The adapters push the query to the engine

The remote mode hands your callback the whole request and leaves the translation to you. A pushdown adapter inverts that: you declare what your engine can answer, and the grid works out what to send and finishes the rest itself. Four adapters ship, and none of them carries an engine, so the bundle is the same whether you use one or not.

AdapterBack endWhat it pushes down
odataAdapterAny OData v4 service.The full filter tree, multi-column sort, paging and the count.
duckdbAdapterDuckDB, in the browser over Parquet or on a server.The full filter tree, multi-column sort, paging and the count, in one query.
restAdapterAn ordinary JSON endpoint.Paging and sort by default; filtering when you declare the operators it applies.
dfqlAdapterThe DemandFlow query API.A single substring filter and the count; the grid does the rest.

An adapter declares its capabilities and the grid pushes only what it claims, finishing anything the engine cannot do in the browser. That split is reported after every query through source.lastPlan(), so "server-side" is something you can watch rather than something you have to take on trust. The pushdown guide covers the split rules in full.

Choosing one

  • Your data fits in the browser. Use memory. It is the default, it needs no back end, and it sorts, filters and groups a million rows locally. Do not reach for a server source before you have measured that this is too slow, because most data is not.
  • It is too large to hold, and you have an API. Use remote and let the server do the sort, filter and grouping, or point a pushdown adapter at the engine and skip writing the translation.
  • Your server paginates and nothing else. Use paged: a page per request, with the scrollbar becoming exact once a total arrives.
  • The rows arrive over time. Use stream, which renders the first chunk immediately and settles into a memory source when the stream completes.
  • You want a summary of a grid you already have. Use derived, which follows the source's filter so a whole dashboard moves from one control.

See them running

Each mode and the two flagship adapters have a live demo that generates or fetches its own data: