@querry-kit/nuxt GitHub

Table

The headless table composable owns Query Kit request state, but consumers retain their table renderer, router, and storage adapters.

UseTableOptions

interface UseTableOptions<
  TItem extends Record<string, unknown>,
  TColumn extends TableColumnInput<TItem, object> = TableColumnInput<TItem>,
> {
  api: AxiosInstance;
  endpoint: string;
  persistenceKey?: string;
  name?: string;
  columns: Ref<readonly TColumn[]>;
  staticFields?: string[];
  staticInclude?: Record<string, unknown>;
  staticFilter?: MaybeRef<Record<string, unknown> | undefined>;
  isArchived?: boolean;
  defaultItemsPerPage?: number;
  storage?: StorageLike;
  routePage?: RoutePageRef;
  identityKey?: keyof TItem & string;
  onInitialized?: () => void | Promise<void>;
  onRefreshed?: (items: TItem[]) => void | Promise<void>;
  onError?: (error: unknown) => void | Promise<void>;
}
PropertyPurpose
api, endpoint, columnsRequired client, endpoint, and reactive renderer-aware column definitions.
persistenceKey / nameNamespace local table preferences; name is retained for compatibility.
staticFields, staticInclude, staticFilter, isArchivedFields and conditions merged into every Query Kit request.
defaultItemsPerPage, storage, routePageInitial size and optional persistence/URL adapters.
identityKeyRow property used by updateRow; defaults to id.
onInitialized, onRefreshed, onErrorLifecycle callbacks for the initial request, latest successful request, and latest failure.

useTable

function useTable<
  TItem extends Record<string, unknown>,
  TColumn extends TableColumnInput<TItem, object> = TableColumnInput<TItem>,
>(options: UseTableOptions<TItem, TColumn>): TableState<TItem, TColumn>;

Creates portable, headless state for a remote Query Kit table. A new request version is assigned on every refresh, so an older response cannot overwrite newer data.

const table = useTable<Book>({
  api,
  endpoint: 'books',
  persistenceKey: 'books',
  columns,
  routePage,
});

await table.initialize();
Returned valueMeaning
page, itemsPerPageCurrent pagination; page can be synchronized through routePage.
sorting, filteringPersisted Query Kit UI state.
columnOrder, columnVisibility, columnPinningPersisted renderer metadata.
columns, fields, queryParamsDerived visible columns, fields projection, and outgoing request.
items, totalItems, totalPages, loading, errorCurrent response and request state.
initialize()Syncs the initial page, fetches, then runs onInitialized.
refresh()Fetches the current request; only the latest response is accepted.
updateRow(row)Shallow-merges the matching row without refetching.

Server-backed nested groups

useGroupedTable loads complete groups independently of ordinary list pagination. Each header shows the count of all matching accessible records. Child groups and leaf records load only when opened; each branch has its own continuation state.

import { useGroupedTable } from '@querry-kit/nuxt';
import type { GroupPath } from '@querry-kit/nuxt/types';

const table = useGroupedTable<Task>({
  api,
  endpoint: 'tasks',
  persistenceKey: 'tasks',
  columns,
  groupableFields: [
    { value: 'status', label: 'Status' },
    { value: 'category', label: 'Category' },
  ],
  defaultGrouping: ['status', 'category'],
  groupBatchSize: 25,
  defaultItemsPerPage: 25,
});

await table.initialize();
await table.loadMoreGroups(); // more root groups
// For a rendered group node:
await table.toggleGroup(node.path);
await table.loadMoreGroups(node.path); // child groups at an intermediate level
await table.loadMoreItems(leaf.path); // records at the final level

groups is a computed ref. root and each node expose children, items, loading, error, and hasMore. Nodes also expose key, typed path, field, value, count, expanded, and loaded. Use node.key as the renderer key and preserve node.path as data, rather than building string paths from display labels.

The composable returns grouping, sorting, filtering, itemsPerPage, columns, fields, columnOrder, columnVisibility, and columnPinning using existing table preference conventions. Only the grouping selection is persisted; expansion and loaded data remain in memory. Empty grouping loads a flat list through loadMoreItems([]). refresh() discards cached groups and resets expansion. Changing filters, grouping, sorting, fields, or page size invalidates pending results. Errors remain local to the affected branch; call its load-more action again to retry. Callbacks onInitialized, onRefreshed (the branch's accumulated records), and onError are optional. A unique identityKey defaults to id and provides a stable record-sort tie-breaker. Group keys sort ascending; grouping fields can be reordered.

The API client also exposes groupBy<TResult>(query), returning an Axios response whose data is TResult[], from GET /:resource/group-by. A request uses bracket notation for by, ordering and selectors; where is JSON encoded to preserve scalar key types. Group continuation fetches one lookahead group and does not need a total group count. There is no global record page in grouped mode.

Configure the backend routes as described in the Nest Query Service grouping guide: use the same static/tenant/access constraints for counts and records, allowlist scalar fields, enable preserveJsonWhereTypes on the service and, when used, on QueryTransformPipe. Client filtering is not authorization. Null keys remain real group values; relations, date intervals, and snapshot-consistent pagination are outside this API. After data mutations, call refresh() to reload counts and membership.