Skip to content

Controller & events

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

import { type TableController } from '@inertiax/core';
import { InertiaX } from '@inertiax/react-inertia';
import { useRef } from 'react';
export function UsersTable() {
const controllerRef = useRef<TableController>(null);
return (
<>
<button onClick={() => controllerRef.current?.refresh()}>
Refresh users
</button>
<InertiaX prop="users" controllerRef={controllerRef} />
</>
);
}

The ref is set while the renderer owns the session and cleared on unmount.

Declare @inertiax/core as a direct dependency when importing its controller or event 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; 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() Immediately replay the state from the current failed request

For example:

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.

retry() is available only while request status is error. It creates a fresh request identity and uses the failed request’s state, even if other state has since changed locally. 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:

<InertiaX
prop="users"
table={{
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 eight 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, and state
requestStart An integration request begins; includes request with its identity, reason, state, request key, and cancellation signal
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.

Custom content inside the renderer can subscribe through hooks:

import { useTableController, useTableState } from '@inertiax/react';
function SelectionSummary() {
const state = useTableState();
const controller = useTableController();
return (
<button
disabled={state.selection.length === 0}
onClick={() => controller.clearSelection()}
>
Clear {state.selection.length} selected rows
</button>
);
}

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.

For operations using selected IDs, see Authorization & security.