Lattice Grid Buy a licence

developer guide

Connect a Data Grid to Your Data: Sources and Adapters

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. Eight 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.
clickhouseAdapterClickHouse, over its HTTP interface.The full filter tree, multi-column sort, paging, the count, grouping and pivoting, every value a typed query parameter.
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.
splunkAdapterSplunk Enterprise 9 and Splunk Cloud.The filter tree as SPL, a multi-column sort, paging, the count, and a time filter as the search's own window.
elasticsearchAdapterElasticsearch and OpenSearch.The filter tree as Query DSL, sort with missing placement, paging past the result window, the count, and grouped sum, avg, min, max and distinct.
graphqlAdapterAny GraphQL endpoint.The window and the total by default; a filter and sort too, once you declare the operators your query supports.

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.

Bringing data in from a system that is not a database

Not every source is reached the same way. Reading Splunk covers both routes: splunkAdapter reads a search live over Splunk's own REST API, filter and sort turned into SPL, or a search exported as CSV, JSON or NDJSON, from the UI, the CLI or the REST API, loads through grid.import or createUrlSource like any other file. See how to load a CSV file into a data grid for a runnable example, fetched by URL or picked from disk.

Build the live version first

When the data will arrive over a socket but the backend does not exist yet, the mock socket stands in for it: an opening snapshot, then a stream of changes on a timer, all from a seeded feed, so you can build and demonstrate a real-time screen with no server. It presents the same surface as a real WebSocket, so the code that reads it does not change when you go live. It pairs with the streaming source and a routed dashboard, feeding one live stream to a whole screen at once.

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: