Skip to content

DataTableForObservableQuery

DataTableForObservableQuery displays the result of a generated Arc observable query and updates the rows when the underlying data changes.

DataTableForObservableQuery subscribes to an IObservableQueryFor query with server-side paging and re-renders the current page whenever the server pushes a new result. Its props are the same as DataTableForQuery.

  • Automatic data updates
  • Server-side pagination, 20 rows per page
  • Single or multiple row selection
  • Sorting, column filters, and a global search box, applied to the loaded page or, with serverQuery, to the complete result on the server
  • Custom column templates
  • Same props as DataTableForQuery
import { DataTableForObservableQuery, Column } from '@cratis/components/DataTables';
import { AllAuthors } from './Author'; // generated Arc observable query proxy
export function LiveAuthors() {
return (
<div style={{ height: '480px' }}>
<DataTableForObservableQuery query={AllAuthors} emptyMessage='No authors yet' dataKey='id'>
<Column field='name' header='Name' sortable />
</DataTableForObservableQuery>
</div>
);
}

When a new author is added to the read model on the server, the row appears without a reload.

Give the table a parent with a definite height. It measures its container and sizes the scrolling row area from that height, with a minimum of 200 pixels.

Same as DataTableForQuery, but the query must derive from ObservableQueryFor.

  • query: Constructor of the generated observable query (derives from ObservableQueryFor)
  • emptyMessage: Message shown when there are no rows to display
  • filteredEmptyMessage: Message shown instead of emptyMessage when a search or column filter is active and no rows are shown. Defaults to messages.dataTable.filteredEmpty (No rows on this page match the search.) or, with serverQuery, messages.dataTable.filteredEmptyServer (No rows match the search.)
  • onGlobalFilterChange: Called with the search text each time it changes
  • children: Column elements
  • queryArguments: Arguments for the query. The table resubscribes when any argument changes.
  • loadingMessage, failureMessage, unauthorizedMessage: Optional React content for the loading, failed, and unauthorized states; each overrides its messages.dataTable provider message and English default
  • dataKey: Row property used as stable identity for selection
  • selection: Currently selected row (controlled)
  • onSelectionChange: Called with { value, originalEvent } when the user selects a row
  • selectionMode: 'single' (default) or 'multiple'. Multiple selection also needs a Column with selectionMode='multiple'; see Column Configuration: Selection
  • selectedItems: Currently selected rows in multiple mode (controlled)
  • onSelectedItemsChange: Called with the full selected set when a multiple selection changes
  • globalFilterFields: Row fields searched by a search box above the table. The box is shown only when this is set.
  • globalSearchPlaceholder: Search-input placeholder. Falls back to the CratisComponentsProvider’s messages.dataTable.search, then 'Search…'
  • globalSearchAriaLabel: Accessible search-input name; localize independently from the placeholder. Falls back to the provider’s messages.dataTable.searchAriaLabel, then 'Search table'
  • defaultFilters: Initial filter configuration (a DataTableFilterMeta)
  • serverQuery: Opts the table into server mode, where the server sorts, filters, and searches the complete result before paging. See Server mode.
  • clientFiltering: Deprecated compatibility prop; accepted but ignored. Without serverQuery, filtering is always scoped to the loaded page
  • className / pt: Extra class and stable part attributes for the table
  • paginatorClassName / paginatorPt / paginatorAriaLabels: styling and explicit localization overrides for the paginator; accessible names default from CratisComponentsProvider messages

ptOptions, unstyled, and paginatorPtOptions are deprecated and have no effect.

Filtered columns use the same filterLabels localization and filterElement custom-editor seams as DataTableForQuery. See Column Configuration. Without serverQuery, filters are applied client-side to the currently loaded observable-query page, while pagination continues to use the server-reported totals.

The deprecated clientFiltering prop remains accepted only for source compatibility and does not change that scope. For complete-result sorting, filtering, and search, set serverQuery; the table then sends them to the server, which applies them before paging and pushes the matching page. It works the same way as for snapshot queries; see Server mode.

While the first observable result is performing without rows, the table shows a loading row with a status announcement instead of emptyMessage. If a required argument is missing, Arc does not subscribe and this loading row remains until every required argument has a value. During a refetch with rows, it keeps the rows and marks the table busy. An unauthorized result takes precedence over a failed result; either displays a failure row with an alert instead of an empty state. A result that settles without success also shows the failure row. A successful empty result still shows emptyMessage. Set loadingMessage, failureMessage, or unauthorizedMessage to customize these states. See Loading, empty, and failed queries for the state rules and defaults.

The table subscribes to the observable query when it mounts and unsubscribes when it unmounts. It updates the display when the server pushes a new result, for example when:

  • New items are added
  • Existing items are modified
  • Items are removed

Each update replaces the row objects. Set dataKey so the selected row stays highlighted across updates. Sorting and filters are kept and applied to the new rows.

Use body to render a cell with other components. Type the column with Column<Row> so the renderer receives your row type:

import { DataTableForObservableQuery, Column } from '@cratis/components/DataTables';
import { Tag } from '@cratis/components/Display';
import { AllAuthors, type Author } from './Author';
export function LiveAuthors() {
return (
<div style={{ height: '480px' }}>
<DataTableForObservableQuery query={AllAuthors} emptyMessage='No authors yet' dataKey='id'>
<Column field='name' header='Name' sortable />
<Column<Author>
header='Status'
body={(author) => <Tag severity='success' value={`Registered: ${author.name}`} />}
/>
</DataTableForObservableQuery>
</div>
);
}
import { useState } from 'react';
import { DataTableForObservableQuery, Column } from '@cratis/components/DataTables';
import { AllAuthors, type Author } from './Author';
export function SelectableAuthors() {
const [selected, setSelected] = useState<Author | null>(null);
return (
<DataTableForObservableQuery<AllAuthors, Author, object>
query={AllAuthors}
emptyMessage='No authors yet'
dataKey='id'
selection={selected}
onSelectionChange={(event) => setSelected(event.value)}
>
<Column field='name' header='Name' />
</DataTableForObservableQuery>
);
}

The explicit type arguments type event.value as Author | null; without them TypeScript infers the row as object.

Observable queries continuously listen for updates. Consider:

  1. Subscription cleanup: Handled automatically by the component
  2. Update frequency: Design queries to minimize unnecessary updates
  3. Data size: Observable queries work best with moderate-sized datasets
  4. Network traffic: Updates are pushed, reducing polling overhead

Use an observable table when users need to see changes made elsewhere without reloading, such as:

  • Operational dashboards and status boards
  • Work queues shared by several users
  • Lists that other parts of the app change while the list is open

Keyboard and accessibility behavior is the same as DataTableForQuery.

Integrates with:

  • @cratis/arc/queries for observable query support
  • @cratis/arc.react/queries for React observable hooks
  • semantic Cratis data table