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-tableOverview
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):
data— pre-resolved array.collection— TanStack DB collection from@docyrus/tanstack-db-generator.- Default —
GET /v1/apps/:appSlug/data-sources/:dataSourceSlug/itemsvia the@docyrus/api-clientyou pass asclient.
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 param | Source | Notes |
|---|---|---|
columns | activeView.columnVisibility + dataSource.fields | Comma-separated slugs of all visible fields, with id always first. Omitted if no fields are loaded yet. |
orderBy | activeView.sorting (TanStack ColumnSort[]) | Mapped to [{ field, direction: 'asc' | 'desc' }, …]. Omitted when empty. |
filters | activeView.filterQuery + side panel RuleGroupType + toolbar filter rules | All sources are ANDed together. The shape matches IQueryFilterGroup. Omitted when no source produces any rules. See Side panel filters. |
filterKeyword | toolbar search input (debounced) | Trimmed; omitted when empty. |
limit, offset | defaultLimit option (default 100) + 0 | Override via listParams. |
| anything else | listParams | Spread 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.
| Option | Type | Default | Description |
|---|---|---|---|
data | Array<TData> | — | Pre-resolved rows. Skips the internal items query. |
collection | { list: (params?) => Promise<…> } | — | TanStack DB collection. list(resolvedListParams) is called inside useQuery. |
listParams | DocyrusDataGridListParams | — | Extra query params merged on top of the view-derived payload. |
defaultLimit | number | 100 | Default page size when no limit is supplied via listParams. |
enableItemsQuery | boolean | true when no data | Toggle the internal items query. |
showSelectColumn | boolean | true | Show the row-select checkbox column as the first column. |
enableRowNumbers | boolean | true | Show 1-based row numbers on the select column when not selected. |
selectColumn | ColumnDef<TData> | — | Override the default select column entirely. |
actionsColumn | ColumnDef<TData> | — | Optional actions column rendered as the second column. |
extraColumns | Array<ColumnDef<TData>> | — | Extra columns prepended after the select + actions columns. |
inferColumnsFromData | boolean | false | Derive 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 / enableReloadButton | boolean | true | Toggle individual toolbar items. The toolbar filter menu (enableFilterMenu) and the side filter panel (enableSideFilters) are independent — both can be active at the same time. |
enableSideFilters | boolean | false | Activate 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. |
sideFiltersConfig | DocyrusDataTableSideFiltersConfig<TData> | — | Configuration for the side panel. Required when enableSideFilters is true. See Side panel filters. |
sideFiltersDefaultExpanded | boolean | true | Initial 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. |
sideFiltersExpanded | boolean | — | 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. |
sideFiltersWidth | number | string | 280 | Width of the side panel when expanded. Number → px. |
enableServerExportMenu | boolean | true | Show the server-side data export dropdown in the toolbar. |
serverExportLimit | number | 10000 | Row cap forwarded to the server export endpoint. |
onReload | () => void | — | Called when the reload button is clicked, after the items query refetches. |
searchPlaceholder | string | 'Search...' | Placeholder for the toolbar search input. |
searchDebounceMs | number | 300 | Debounce in ms before the search input is sent as filterKeyword. |
toolbarClassName | string | — | Extra className for the toolbar root. |
toolbarStartContent / toolbarEndContent | ReactNode | — | Custom nodes prepended/appended to the built-in toolbar. |
tableClassName / tableContainerClassName | string | — | Forwarded to <DataTable>. |
emptyText | string | — | Empty-state text forwarded to <DataTable>. |
Return Value
| Property | Type | Description |
|---|---|---|
table | Table<TData> | TanStack Table instance — pass to <DataTable>. |
tableProps | Omit<DataTableProps, 'table'> | Spread onto <DataTable table={table} {...tableProps} />. |
toolbar | ReactNode | Pre-wired toolbar element ready to render above the table. |
sidePanel | ReactNode | Vertical view-picker panel. null for 'horizontal-tabs'/'dropdown' view variants. |
sideFilters | ReactNode | Side-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. |
sideFiltersExpanded | boolean | Current expanded state of the side filter panel. |
setSideFiltersExpanded | (expanded: boolean) => void | Programmatically toggle the side filter panel. |
sideFiltersQuery | RuleGroupType | undefined | Current RuleGroupType emitted by the side panel — already merged into resolvedListParams.filters. |
items | Array<TData> | Resolved rows passed to the table. |
resolvedListParams | DocyrusDataGridListParams | The list params actually sent to the backend (after merging view state, search, side panel filters, and listParams). |
pagingMode | 'standard' | 'virtual-scroll' | undefined | Resolved paging mode for the active view. |
reload | () => void | Triggers refetch of the data source, views, and items queries plus the optional onReload callback. |
views | SavedDataGridView[] | Saved views mapped from the backend shape. |
fields | FullField[] | Fields mapped for react-querybuilder filter editor. |
dataSource | DataSource | undefined | Raw data source metadata response. |
activeViewId | string | Id of the currently active view. |
setActiveViewId | (viewId: string) => void | Programmatically switch views. Persisted per user (follows persistState storage when enabled, localStorage otherwise). |
isLoading | boolean | true until all queries (data source, views, items) have resolved. |
error | Error | null | First error from any of the queries. |
refetch | () => void | Alias 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
sideFiltersDefaultExpandedcontrols the initial state. Defaulttrue.- 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>
| Field | Type | Default | Description |
|---|---|---|---|
columnsConfig | ReadonlyArray<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. |
defaults | SideFilterDefaults | — | Per-column UI hints (collapsed, mode, showMoreThreshold, hidden, sticky, etc.). |
sections | ReadonlyArray<SideFilterSectionGroup> | — | Optional grouping of filter sections into named blocks. |
variant | 'default' | 'bordered' | 'compact' | 'default' | Visual variant for the panel container. |
title | ReactNode | 'Filters' | Header title. Pass null to hide. |
showActiveChips | boolean | true | Show the active-filter chip strip below the header. |
showClearAll | boolean | true | Show the "Clear all" button next to the title when any filter is active. |
searchable | boolean | string | false | Render a search input above the sections that drives the first text-typed column (or a column id you pass explicitly). |
clearAllLabel | string | 'Clear all' | Override for the "Clear all" label. |
clearLabel | string | 'Clear' | Override for the per-section "Clear" label. |
locale | Locale | 'en' | Locale forwarded to the underlying filter UI. |
className | string | — | Extra className for the panel root. |
collapseAriaLabel | string | 'Collapse filters' | Override for the collapse-button accessible label. |
expandAriaLabel | string | 'Expand filters' | Override for the expand-button accessible label. |
collapsedWidth | number | string | 36 | Width 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>
);
}