YaYaw TableYaYaw Table

Props and usage reference for the unified DataTable component

DataTable Reference

The single entry-point component: you pass config and actions as props; it composes the provider, UI, and table logic internally.

import { DataTable } from "@/components/ui/yayaw-table";

For a minimal working setup you need tableType, getTableConfig, and getTableActions. See Provider & Setup for the full list and Next.js layout (NuqsAdapter, QueryClientProvider).

If no QueryClient is available from context (and no explicit queryClient prop is provided), DataTable now throws an explicit runtime error.

Props

List of available props for configuring DataTable.

tableType

The type of table configuration to use. Used to resolve config and actions (e.g. getTableConfig("products"), getTableActions("products")).

<DataTable tableType="products" />

Required: true

tableId

Stable table instance id for URL state, cache keys, selection, pagination, and toolbar state. Defaults to tableType.

Use it when one table instance displays several business models but should still share one URL/cache/selection surface.

Type: string

formType

Default form configuration type for create/edit forms. Defaults to tableType. Table config can still override it with form.createFormType, form.editFormType, or form.resolveEditFormType(row).

Type: string

getTableConfig

Function that returns table and column configuration for the given tableType. Required for the table to know columns, sort, visibility, and options. See Configuration and Setup.

Type: (tableType: string) => Config | undefined

getTableActions

Function that returns actions (list, create, update, delete, bulk, etc.) for the given tableType. list is required for server-driven data. Can be Server Actions in Next.js. See Actions and Server-side & Server Actions.

Type: (tableType: string) => TableActions | undefined

getFormConfig

Function that returns form field definitions for create/edit and bulk edit dialogs. Optional but needed for forms.

Type: (formType: string, ctx?: FormConfigContext) => FormConfig | undefined

ctx includes { mode, tableId, tableType, formType, row, initialData, values }, so form fields can change for edit rows or for current create-form values.

queryClient

Optional explicit TanStack Query client instance. YaYaw Table no longer creates one internally. In most apps you should provide a shared app-level QueryClientProvider and omit this prop.

Type: QueryClient

className

Additional class names applied to the root wrapper.

Type: string | Default: undefined

loadingOverlay

Custom loading overlay to display while data is loading.

Type: React.ReactNode | Default: internal spinner overlay

<DataTable tableType="products" loadingOverlay={<MySpinner />} />

onRowSelectionChange

Callback fired when selected rows change.

Type: (rows: Row<Record<string, unknown>>[]) => void | Default: undefined

onBulkEdit

Handle bulk edit of selected rows.

Type: (rows: Row<Record<string, unknown>>[]) => BulkActionResult | void | Promise<BulkActionResult | void> | Default: uses provider actions

onBulkDelete

Handle bulk deletion of selected rows.

Type: (rows: Row<Record<string, unknown>>[]) => BulkActionResult | BulkDeleteExecutionOutcome | void | Promise<...> | Default: uses provider bulkDelete / delete

onBulkCopy

Handle bulk duplication of selected rows.

Type: (rows: Row<Record<string, unknown>>[]) => BulkActionResult | void | Promise<BulkActionResult | void> | Default: internal clipboard copy

onExport

Override toolbar export behavior. Called with all rows matching current search/filters/sort state. If provided, default CSV download is skipped.

Type: (rows: Record<string, unknown>[]) => void \| Promise<void> | Default: internal CSV export

onBulkExport

Override bulk export behavior. Called with selected rows. If provided, default CSV download is skipped.

Type: (rows: Row<Record<string, unknown>>[]) => void \| Promise<void> | Default: internal CSV export

customBulkActions

Inject custom actions in the selected-row bulk actions menu. Supports a static array or a callback receiving live selected-row context.

Type: BulkAction<TData>[] | ((ctx: BulkActionContext<TData>) => BulkAction<TData>[]) | Default: undefined

Custom actions render after the built-in export action and before the built-in delete action. A custom action can be disabled from the current selection context, can ask for confirmation, and can return a BulkActionResult to control menu closing, selection clearing, and user feedback.

type BulkActionContext<TData> = {
  selectedRows: Row<TData>[];
  selectedOriginalRows: TData[];
  selectedCount: number;
};

type BulkAction<TData> = {
  id: string;
  label: string;
  icon: ComponentType<{ className?: string; size?: number }>;
  onClick: (ctx: BulkActionContext<TData>) =>
    | BulkActionResult
    | void
    | Promise<BulkActionResult | void>;
  disabled?: boolean | ((ctx: BulkActionContext<TData>) => boolean);
  variant?: "default" | "destructive";
  confirm?: {
    title?: string | ((ctx: BulkActionContext<TData>) => string);
    description?: string | ((ctx: BulkActionContext<TData>) => string);
    confirmLabel?: string | ((ctx: BulkActionContext<TData>) => string);
    cancelLabel?: string | ((ctx: BulkActionContext<TData>) => string);
  };
};

toolbarActions

Inject custom actions in the main toolbar. Supports a static array or a callback receiving live toolbar context.

Type: ToolbarAction[] | ((ctx: ToolbarActionContext) => ToolbarAction[]) | Default: undefined

toolbarActionsPlacement

Control where custom toolbar actions are rendered relative to built-in actions.

Type: "before-create" | "between-create-export" | "after-export" | Default: "between-create-export"

ToolbarAction and ToolbarActionContext

type ToolbarAction = {
  id: string;
  label: string;
  icon?: ReactNode;
  onClick: (ctx: ToolbarActionContext) => void | Promise<void>;
  disabled?: boolean | ((ctx: ToolbarActionContext) => boolean);
  loading?: boolean;
  variant?: "default" | "outline" | "secondary" | "ghost" | "destructive";
  showInIconMode?: boolean; // default true
  tooltip?: string;
};

type ToolbarActionContext = {
  tableId: string;
  actionsAsIcons: boolean;
  isMobile: boolean;
  isCreateEnabled: boolean;
  isExportEnabled: boolean;
  isExporting: boolean;
  hasListAction: boolean;
  selectedRows: Row<Record<string, unknown>>[];
  selectedOriginalRows: Record<string, unknown>[];
  selectedRowIds: string[];
  selectedCount: number;
  tableActions?: TableActions;
};

Multi-Model Table

A single table can keep one tableId while resolving table config and form config separately:

<DataTable
  tableId="cms-entries"
  tableType="content-index"
  formType="content-entry"
  getTableConfig={(tableType) => ({
    ...configs[tableType],
    form: {
      createFormType: "content-entry",
      resolveEditFormType: (row) => `${row.modelId}-entry`,
    },
  })}
  getFormConfig={(formType, ctx) =>
    buildEntryForm({
      formType,
      modelId: String(ctx?.values?.modelId ?? ctx?.row?.modelId ?? ""),
    })
  }
  customBulkActions={(ctx) => [
    {
      id: "publish-selected",
      label: "Publish",
      icon: Send,
      disabled: ctx.selectedCount === 0,
      onClick: async () => publishEntries(ctx.selectedOriginalRows),
    },
    {
      id: "archive-selected",
      label: "Archive",
      icon: Archive,
      variant: "destructive",
      disabled: ctx.selectedCount === 0,
      confirm: {
        title: "Archive selected entries?",
        description: `Archive ${ctx.selectedCount} selected entries.`,
      },
      onClick: async () => archiveEntries(ctx.selectedOriginalRows),
    },
  ]}
/>

Here cms-entries owns URL/cache/selection, content-index owns the table columns and filters, and each row can open its own edit form type.

enableAdvancedFilters

Enable advanced filters UI when available in your configuration.

Type: boolean | Default: false

columnTypeMapping

Map dynamic backend types to internal column renderer types.

Type: Record<string, 'text' | 'number' | 'date' | 'option' | 'multiOption'> | Default: {}

Usage

<DataTable
  tableType="products"
  loadingOverlay={<MySpinner />}
  onRowSelectionChange={(rows) => console.log(rows)}
  onBulkDelete={(rows) => console.log("delete", rows.length)}
  onExport={(rows) => console.log("export all", rows.length)}
  onBulkExport={(rows) => console.log("export selected", rows.length)}
/>

Custom action in text mode:

<DataTable
  tableType="products"
  toolbarActions={[
    {
      id: "recalculate-prices",
      label: "Recalculate prices",
      onClick: async () => {
        await recalculatePrices();
      },
      variant: "secondary",
    },
  ]}
/>

Custom action in icon mode with tooltip and explicit placement:

<DataTable
  tableType="products"
  toolbarActions={(ctx) => [
    {
      id: "recalculate-prices",
      label: "Recalculate prices",
      tooltip: "Recalculate prices",
      icon: <RefreshCw className="h-4 w-4" />,
      disabled: () => !ctx.hasListAction || ctx.isExporting,
      onClick: async () => {
        await recalculatePrices();
      },
    },
  ]}
  toolbarActionsPlacement="between-create-export"
/>

Backward compatibility: if toolbarActions is omitted, toolbar behavior stays unchanged.

See also:

On this page