Skip to content

Controller & events

Use the controller to drive a table from application UI. Use events to observe accepted changes.

A successful-table region receives the controller. For example, add a refresh button above the toolbar:

React

import type { TableRegionProps } from '@inertiax/react-table';
import { AppTable } from '@/components/app-table';
function RefreshUsers({ controller, status }: TableRegionProps) {
return (
<button
type="button"
disabled={status.type === 'pending' || status.type === 'scheduled'}
onClick={() => controller.refresh()}
>
Refresh users
</button>
);
}
<AppTable prop="users" customization={{ regions: { beforeToolbar: RefreshUsers } }} />;

This region only mounts when a successful result exists. During an invalid-link problem, the table displays its reset control instead.

For controls outside the table, pass a React ref through controllerRef:

React

import type { TableController } from '@inertiax/table-core';
import { AppTable } from '@/components/app-table';
import { useRef } from 'react';
function UsersTable() {
const controllerRef = useRef<TableController>(null);
return <AppTable prop="users" controllerRef={controllerRef} />;
}

The ref is set while the renderer owns the session, including during a problem, and cleared on unmount. A non-null ref alone does not mean state-dependent operations are available; see the operation contracts below. Declare @inertiax/table-core as a direct dependency when importing its types.

Operation Argument or result
getState() Immutable TableState snapshot
setPage(page) Positive page number supported by the current result
setPageSize(pageSize) One enabled page-size option; resets page to 1
setSorting(sorting) Ordered array of { key, direction: 'asc' | 'desc' }; resets page to 1
setFilters(filters) { operator: 'and' | 'or', conditions: [...] }; each condition is { key, clause, value } or a nested group, with value: null for a clause that takes none; resets page to 1
setSearch(search) String, trimmed by the session; resets page to 1
getSelection() Immutable array of current-result string or JavaScript-safe integer row IDs
setSelection(selection) Current-result string or JavaScript-safe integer row IDs; use clearSelection() for an empty selection
getColumnVisibility() Immutable { [columnKey]: boolean } map
setColumnVisibility(visibility) Partial visibility map merged into current state; keys must be declared, toggleable columns
resetColumnVisibility() Restore visibility from the current Column definitions
refresh() Immediately request the current server state
retry() Replay the current failed request, including a failed reset
resetProblem() Explicitly reset invalid external settings when the problem and integration support it

During a table problem, no successful state exists. State getters, setters, and refresh() reject with INERTIAX_TABLE_SESSION_STATE_UNAVAILABLE. Read useTableSnapshot() and check outcome === 'problem'; use canReset to decide whether to show resetProblem(). A problem response emits requestProblem with request, componentId, and problem, rather than requestSuccess.

For example:

React

controller.setSorting([
{ key: 'role', direction: 'asc' },
{ key: 'name', direction: 'desc' },
]);
controller.setFilters({
operator: 'and',
conditions: [{ key: 'status', clause: 'equals', value: 'active' }],
});
controller.setSelection(['user_42']);
controller.setColumnVisibility({ email: false });

These operations validate state and use the configured integration. Do not mutate the returned state object or construct Inertia URLs yourself. Search and filter requests use the configured debounce; page, page-size, sorting, refresh, and retry requests are immediate. Selection and column visibility are client-only and do not dispatch a request.

Inside a custom renderer region, use useTableController() instead of threading the ref.

State setters also work from a child component’s layout effect or a controller callback ref, including when Activity or Suspense restores the table. Validation and state reads are synchronous; any required request waits until the table connects to its integration.

retry() is available only while request status is error. It creates a fresh request identity and replays its state or reset fields. Calling it while idle, scheduled, pending, successful, or externally invalid fails with INERTIAX_TABLE_SESSION_RETRY_UNAVAILABLE; a disposed session instead rejects every controller operation as disposed. Use refresh() when you want the current state rather than the failed request’s state.

Event handlers can be application-scoped or component-scoped:

React

<AppTable
prop="users"
customization={{
events: {
selectionChange(event) {
console.log(event.selection);
},
requestError(event) {
reportError(event.error);
},
copySuccess(event) {
analytics.track('table_cell_copied', {
column: event.columnKey,
row: event.rowId,
});
},
},
}}
/>

The nine supported event keys and their distinguishing payloads are:

Event Emitted when and payload
stateChange Any accepted transition, browser-location update, or result reconciliation changes state; includes componentId, source, previousState (null when recovering from a problem), and state
requestStart An integration request begins; includes request with its identity, reason, state (or reset fields), request key, and cancellation signal
requestProblem request, componentId, problem; valid problem response without a successful result
requestSuccess The accepted latest request succeeds; includes request and result
requestError The accepted latest request fails; includes request and error
selectionChange A controller or built-in UI selection transition is accepted; includes componentId, selection, and state
columnVisibilityChange A controller or built-in UI visibility transition is accepted; includes componentId, columnVisibility, and state
copySuccess A cell value is copied; includes componentId, columnKey, and rowId
copyError Copying fails; includes the copy fields plus error

A result can prune selection or reset visibility while reconciling new definitions. Observe that through stateChange with source: 'result'; it is not a second controller transition and does not also emit selectionChange or columnVisibilityChange. Cancelled or stale requests likewise do not emit success or error events.

Provider and component handlers both observe their scope. When both exist, the provider handler runs first and the component handler runs second. Events are immutable snapshots. Treat them as notifications; dispatch new state through the controller rather than modifying an event. A handler exception is rethrown asynchronously and does not become a Table request failure.

Successful-table regions can subscribe through hooks:

React

import { useTableController, useTableState } from '@inertiax/react-table';
function SelectionSummary() {
const state = useTableState();
const controller = useTableController();
return (
<button
type="button"
disabled={state.selection.length === 0}
onClick={() => controller.clearSelection()}
>
Clear {state.selection.length} selected rows
</button>
);
}
<AppTable prop="users" customization={{ regions: { beforeToolbar: SelectionSummary } }} />;

useTableSnapshot() returns state, the current result envelope, and status from one immutable snapshot. useTableState(), useTableStatus(), and useTableEnvelope() subscribe to the narrower slice. These hooks must run inside the current Table renderer scope. useTableState() and useTableEnvelope() require a successful outcome; see Custom table layouts when replacing the whole renderer.

For operations using selected IDs, see Authorization & security.

View Markdown