Hooks

useDocyrusDataTable

One-call wiring of a Docyrus data source to a fully configured DataTable + toolbar (DataGridViewSelect, search, filters, group, sort) — including row fetching with view-derived query parameters and an optional side-panel filter rail.

Installation

pnpm dlx @docyrus/cli add @docyrus/hooks-use-docyrus-data-table

Overview

useDocyrusDataTable is the table-flavored sibling of useDocyrusDataGrid. It produces a TanStack Table instance + toolbar wired to the same Docyrus saved-views, filter, and items endpoints, but renders rows through <DataTable> instead of <DataGrid>.

It also generates ColumnDefs automatically from the data source's field metadata, mapping every Docyrus field type to the matching value renderer.

Backend connection

Same three modes as useDocyrusDataGrid (precedence top→bottom):

  1. data — pre-resolved array.
  2. collection — TanStack DB collection from @docyrus/tanstack-db-generator.
  3. Default — GET /v1/apps/:appSlug/data-sources/:dataSourceSlug/items via the @docyrus/api-client you pass as client.

The reload button refetches the items query (modes 2 and 3), the data source + views queries, and any onReload callback you provided.

Tenant-aware formatting

Date / datetime / number cells render through the formatters supplied by <DocyrusTenantProvider> — no per-table wiring required. Mount the provider once near the root of your app and every useDocyrusDataTable call below it picks up the tenant's dateFormat, dateTimeFormat, decimalSeparator, thousandSeparator, decimalPrecision, and timeZone settings automatically.

Resolution order for each cell formatter: explicit prop on useDocyrusDataTable({ formatDate, formatDateTime, formatNumber }) → surrounding useDateFormat() / useNumberFormat() context → cell's own legacy fallback. Apps that haven't adopted <DocyrusTenantProvider> keep their previous behaviour byte-for-byte.

Query parameters sent to the backend

The hook builds a ZodSelectQueryPayload-shaped payload (resolvedListParams) automatically from the active view + the toolbar search input + the optional side-panel filter rail + any listParams overrides:

Backend paramSourceNotes
columnsactiveView.columnVisibility + dataSource.fieldsComma-separated slugs of all visible fields, with id always first. Omitted if no fields are loaded yet.
orderByactiveView.sorting (TanStack ColumnSort[])Mapped to [{ field, direction: 'asc' | 'desc' }, …]. Omitted when empty.
filtersactiveView.filterQuery + side panel RuleGroupType + toolbar filter rulesAll sources are ANDed together. The shape matches IQueryFilterGroup. Omitted when no source produces any rules. See Side panel filters.
filterKeywordtoolbar search input (debounced)Trimmed; omitted when empty.
limit, offsetdefaultLimit option (default 100) + 0Override via listParams.
anything elselistParamsSpread last and wins on conflict.

The fully-resolved payload is exposed back to the consumer as resolvedListParams.

Usage

'use client';

import { useDocyrusAuth } from '@docyrus/signin';

import { DataTable } from '@docyrus/ui/components/data-table';
import { useDocyrusDataTable } from '@docyrus/ui/library/hooks/use-docyrus-data-table';

type OrganizationRow = { id: string; name: string };

export function OrganizationsTable() {
  const { client } = useDocyrusAuth();

  if (!client) return null;

  const { table, tableProps, toolbar } = useDocyrusDataTable<OrganizationRow>({
    client,
    appSlug: 'crm',
    dataSourceSlug: 'organization'
  });

  return (
    <div className="flex h-full flex-col gap-3">
      {toolbar}
      <DataTable table={table} {...tableProps} />
    </div>
  );
}

API Reference

Parameters

The hook accepts every option from useDocyrusDataViewSelect, plus:

DB-free metadata. Inherited from useDocyrusDataViewSelect: dataSource (inject a pre-resolved schema → skips getBySlug), enableDataViews: false (skip the /views fetch), and dataSourceExpand (tune/drop the schema expand param). For system data sources that expose no schema at all, combine data (pre-resolved rows) with inferColumnsFromData below to build columns from the row keys. See DB-free metadata.

OptionTypeDefaultDescription
dataArray<TData>—Pre-resolved rows. Skips the internal items query.
collection{ list: (params?) => Promise<…> }—TanStack DB collection. list(resolvedListParams) is called inside useQuery.
listParamsDocyrusDataGridListParams—Extra query params merged on top of the view-derived payload.
defaultLimitnumber100Default page size when no limit is supplied via listParams.
enableItemsQuerybooleantrue when no dataToggle the internal items query.
showSelectColumnbooleantrueShow the row-select checkbox column as the first column.
enableRowNumbersbooleantrueShow 1-based row numbers on the select column when not selected.
selectColumnColumnDef<TData>—Override the default select column entirely.
actionsColumnColumnDef<TData>—Optional actions column rendered as the second column.
extraColumnsArray<ColumnDef<TData>>—Extra columns prepended after the select + actions columns.
inferColumnsFromDatabooleanfalseDerive table columns from the loaded rows when the data source exposes no schema fields (e.g. system data sources, typically paired with enabled: false) and no extraColumns are supplied. Columns are built from the union of keys in the first rows; identity/label keys (id, name, title, …) lead and audit keys (created_on, …) trail. Ignored when schema fields or extraColumns exist.
mapColumn(field, defaultColumn) => ColumnDef | null—Per-field column override. Return null to skip a field.
enableViewSelect / enableSearchInput / enableFilterMenu / enableGroupMenu / enableSortMenu / enableReloadButtonbooleantrueToggle individual toolbar items. The toolbar filter menu (enableFilterMenu) and the side filter panel (enableSideFilters) are independent — both can be active at the same time.
enableSideFiltersbooleanfalseActivate the side-panel filter rail rendered alongside the table. When true, the hook returns a ready-to-render sideFilters element wired to <DataTableSideFilters> and merges its emitted RuleGroupType into the items request alongside the saved view filter and toolbar filter rules. Requires sideFiltersConfig.
sideFiltersConfigDocyrusDataTableSideFiltersConfig<TData>—Configuration for the side panel. Required when enableSideFilters is true. See Side panel filters.
sideFiltersDefaultExpandedbooleantrueInitial expanded state. When false, the panel mounts collapsed and renders a thin vertical rail with a 90°-rotated "Filters" label that re-expands the panel on click.
sideFiltersExpandedboolean—Controlled expanded state. Pair with onSideFiltersExpandedChange.
onSideFiltersExpandedChange(expanded: boolean) => void—Called when the user toggles the panel via the close icon (top right of the panel header) or the rotated rail button.
sideFiltersWidthnumber | string280Width of the side panel when expanded. Number → px.
enableServerExportMenubooleantrueShow the server-side data export dropdown in the toolbar.
serverExportLimitnumber10000Row cap forwarded to the server export endpoint.
onReload() => void—Called when the reload button is clicked, after the items query refetches.
searchPlaceholderstring'Search...'Placeholder for the toolbar search input.
searchDebounceMsnumber300Debounce in ms before the search input is sent as filterKeyword.
toolbarClassNamestring—Extra className for the toolbar root.
toolbarStartContent / toolbarEndContentReactNode—Custom nodes prepended/appended to the built-in toolbar.
tableClassName / tableContainerClassNamestring—Forwarded to <DataTable>.
emptyTextstring—Empty-state text forwarded to <DataTable>.

Return Value

PropertyTypeDescription
tableTable<TData>TanStack Table instance — pass to <DataTable>.
tablePropsOmit<DataTableProps, 'table'>Spread onto <DataTable table={table} {...tableProps} />.
toolbarReactNodePre-wired toolbar element ready to render above the table.
sidePanelReactNodeVertical view-picker panel. null for 'horizontal-tabs'/'dropdown' view variants.
sideFiltersReactNodeSide-panel filter rail. null when enableSideFilters is false. Renders the full <DataTableSideFilters> panel (with a panel-close icon in the header) when expanded, and a thin vertical rail (with a 90°-rotated "Filters" label) when collapsed.
sideFiltersExpandedbooleanCurrent expanded state of the side filter panel.
setSideFiltersExpanded(expanded: boolean) => voidProgrammatically toggle the side filter panel.
sideFiltersQueryRuleGroupType | undefinedCurrent RuleGroupType emitted by the side panel — already merged into resolvedListParams.filters.
itemsArray<TData>Resolved rows passed to the table.
resolvedListParamsDocyrusDataGridListParamsThe list params actually sent to the backend (after merging view state, search, side panel filters, and listParams).
pagingMode'standard' | 'virtual-scroll' | undefinedResolved paging mode for the active view.
reload() => voidTriggers refetch of the data source, views, and items queries plus the optional onReload callback.
viewsSavedDataGridView[]Saved views mapped from the backend shape.
fieldsFullField[]Fields mapped for react-querybuilder filter editor.
dataSourceDataSource | undefinedRaw data source metadata response.
activeViewIdstringId of the currently active view.
setActiveViewId(viewId: string) => voidProgrammatically switch views. Persisted per user (follows persistState storage when enabled, localStorage otherwise).
isLoadingbooleantrue until all queries (data source, views, items) have resolved.
errorError | nullFirst error from any of the queries.
refetch() => voidAlias for reload.

Side panel filters

When enableSideFilters is true, the hook renders a <DataTableSideFilters> rail alongside the table. The panel emits a RuleGroupType whenever the user adjusts a filter; the hook ANDs it onto the saved view's filterQuery and any toolbar filter menu rules before sending the items request — so side filters, toolbar filters, and the saved view all stack.

The toolbar filter menu (enableFilterMenu) is not auto-hidden; both can be active at the same time.

Collapse / expand

  • sideFiltersDefaultExpanded controls the initial state. Default true.
  • When expanded, a panel-close icon (top right of the panel header) collapses it to a vertical rail.
  • When collapsed, the rail shows a 90°-rotated "Filters" label (with the active-filter count appended) that re-expands the panel on click.
  • For controlled collapse state, pass sideFiltersExpanded + onSideFiltersExpandedChange.

DocyrusDataTableSideFiltersConfig<TData>

FieldTypeDefaultDescription
columnsConfigReadonlyArray<ColumnConfig<TData>>—Required. Filter columns for the panel. Mirrors the columnsConfig accepted by useDataTableFilters / useDataTableSideFilters.
strategy'server' | 'client''server'Filter strategy. Server-paged tables should use 'server' so values surface as RuleGroupType rules; 'client' filters the in-memory dataset.
defaultsSideFilterDefaults—Per-column UI hints (collapsed, mode, showMoreThreshold, hidden, sticky, etc.).
sectionsReadonlyArray<SideFilterSectionGroup>—Optional grouping of filter sections into named blocks.
variant'default' | 'bordered' | 'compact''default'Visual variant for the panel container.
titleReactNode'Filters'Header title. Pass null to hide.
showActiveChipsbooleantrueShow the active-filter chip strip below the header.
showClearAllbooleantrueShow the "Clear all" button next to the title when any filter is active.
searchableboolean | stringfalseRender a search input above the sections that drives the first text-typed column (or a column id you pass explicitly).
clearAllLabelstring'Clear all'Override for the "Clear all" label.
clearLabelstring'Clear'Override for the per-section "Clear" label.
localeLocale'en'Locale forwarded to the underlying filter UI.
classNamestring—Extra className for the panel root.
collapseAriaLabelstring'Collapse filters'Override for the collapse-button accessible label.
expandAriaLabelstring'Expand filters'Override for the expand-button accessible label.
collapsedWidthnumber | string36Width of the collapsed rail. Number → px.

Usage

'use client';

import { useDocyrusAuth } from '@docyrus/signin';

import { DataTable } from '@docyrus/ui/components/data-table';
import { useDocyrusDataTable } from '@docyrus/ui/library/hooks/use-docyrus-data-table';
import { createColumnConfigHelper } from '@docyrus/ui/components/data-table-filter';

type OrganizationRow = { id: string; name: string; status: 'active' | 'archived' };

const dtf = createColumnConfigHelper<OrganizationRow>();

const sideFilterColumns = [
  dtf
    .text()
    .id('name')
    .accessor(row => row.name)
    .displayName('Name')
    .build(),
  dtf
    .option()
    .id('status')
    .accessor(row => row.status)
    .displayName('Status')
    .options([
      { value: 'active', label: 'Active' },
      { value: 'archived', label: 'Archived' }
    ])
    .build()
] as const;

export function OrganizationsTable() {
  const { client } = useDocyrusAuth();

  if (!client) return null;

  const {
    table, tableProps, toolbar, sideFilters
  } = useDocyrusDataTable<OrganizationRow>({
    client,
    appSlug: 'crm',
    dataSourceSlug: 'organization',
    enableSideFilters: true,
    sideFiltersDefaultExpanded: true,
    sideFiltersWidth: 280,
    sideFiltersConfig: {
      columnsConfig: sideFilterColumns,
      strategy: 'server',
      defaults: {
        status: { mode: 'inline-checkbox' }
      }
    }
  });

  return (
    <div className="flex h-full flex-col gap-3">
      {toolbar}
      <div className="flex flex-1 min-h-0">
        {sideFilters}
        <DataTable table={table} {...tableProps} className="flex-1" />
      </div>
    </div>
  );
}

On this page