Server-side data handling
Server-side data handling is available through Grid Pro and uses
providerType: 'remote'.
Use this model when your backend should handle sorting, filtering, and pagination before the current rows are returned to the Grid.
How it works
With the remote provider, Grid tracks the current query state and re-fetches data when sorting, filtering, or pagination changes. This is the only built-in mode intended for true server-side query handling.
Remote data can be configured in two ways:
fetchCallbackfor full control over the request and response handlingdataSourcefor a serialized URL-template based setup
If users can edit cells and those changes should be persisted remotely, add
setValueCallback.
Fetch callback
Grid.grid('container', {data: {providerType: 'remote',fetchCallback: async (query, offset, limit, signal) => {const params = new URLSearchParams({// Request the current slice of rows.offset: String(offset),limit: String(limit),// Forward the current Grid query state.sorting: JSON.stringify(query.sorting.currentSortings || []),filtering: JSON.stringify(query.filtering.modifier?.options || null)});const response = await fetch(`/api/grid?${params}`, { signal });const result = await response.json();return {columns: result.columns,totalRowCount: result.totalRowCount,rowIds: result.rowIds};},setValueCallback: async (columnId, rowId, value) => {await fetch('/api/grid/cell', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ columnId, rowId, value })});},// Optional: rows per fetched chunk.chunkSize: 200,// Optional: LRU cache size for chunks (unset keeps all).chunksLimit: 20,// Optional: request policy for rapid query changes.requestPolicy: 'latest'}});
Use fetchCallback when you want full control over how Grid queries your
backend. The callback receives the current query state plus offset, limit,
and an optional AbortSignal. In the example above, offset and limit are
sent as request parameters, query is used to forward sorting and filtering
state, signal is passed to fetch, and the callback returns the expected
columns, totalRowCount, and optional rowIds.
Data Source helper
Grid.grid('container', {data: {providerType: 'remote',dataSource: {urlTemplate:'/api/grid?page={page}&pageSize={pageSize}&' +'sortBy={sortBy}&sortOrder={sortOrder}&filter={filter}'},// ID of the column that contains stable row IDs. If not set, uses// `rowIds` from the fetch result or generated row indices.idColumn: 'employeeId',setValueCallback: async (columnId, rowId, value) => {await fetch('/api/grid/cell', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ columnId, rowId, value })});}},columnDefaults: {filtering: {enabled: true}},pagination: {enabled: true,pageSize: 25}});
When you provide dataSource, Grid uses the Data Source helper to build request
URLs from query state and to parse the response.
Optional dataSource configuration:
dataSource: {urlTemplate: '/api/grid?foo={foo}&...',templateVariables: {// Extend the template with custom variables.foo: (state) => state.query?.something ?? ''},// Removes empty query parameters from the URL.omitEmpty: true,parseResponse: async (res) => {// Parse the response into { columns, totalRowCount, rowIds? }.const { data, meta } = await res.json();return {columns: data || {},totalRowCount: meta?.totalRowCount || 0,rowIds: meta?.rowIds};},// Request timeout in ms (use 0 to disable).fetchTimeout: 30000}
fetchCallback vs. dataSource
Use fetchCallback when:
- request building must be fully custom
- the backend response needs custom transformation
- you want direct access to the current query state and abort signal
Use dataSource when:
- a URL template is enough
- you want the built-in query serialization for page, page size, sort, and filter
- you want a serializable configuration
Row IDs and editing
Use idColumn when one of the returned columns contains stable row IDs. If it
is not set, the remote provider falls back to rowIds from the response or to
generated row indices.
Use setValueCallback when edited cell values should be saved on the server.
Without it, remote editing cannot be persisted.
Remote vs. connector-backed local
Do not use data.connector when you need the backend to handle sorting,
filtering, or pagination. A connector can fetch remote data, but once it is
loaded into Grid it still uses the local provider model and queries happen
client-side.