Custom types
Use ordinary Column transformers, decorators, Filter Clauses, React regions, and theme operations first. Create a custom type when the wire payload needs a stable semantic identity that a matching frontend renderer can resolve.
What type() means
Section titled “What type() means”The Column key identifies data (role). Its type identifies presentation semantics
(workbench/role). A Filter type similarly identifies the matching input renderer. type() does
not change which source field is queried and does not execute browser code on the server.
Custom identifiers must be namespaced as vendor/name. Built-in names are reserved.
Full-stack custom leaf types
Section titled “Full-stack custom leaf types”A custom column/filter path has two halves:
- Laravel emits namespaced column/filter/Clause identifiers and executes validated server behavior.
- React registers matching cell and filter-input implementations at application or component scope.
The canonical executable implementation is workbench/role in the Laravel workbench:
TextColumn::make('role', 'Role')->type('workbench/role');
TextFilter::make('role', 'Role') ->type('workbench/role-filter') ->clearClauses() ->addClause( Clause::make('workbench/role-equals', 'Has role') ->validateUsing(fn (mixed $value): bool => is_string($value)) ->applyUsing( fn (Builder $query, string $column, string $value): Builder => $query->where($column, $value), DataSourceKind::Eloquent, ), );The custom Column type changes cell resolution. The custom Clause validates and applies the server-side comparison. Declaring supported sources is mandatory for custom data operations. A Collection implementation must be supplied and declared if the operation also supports it.
The matching React component registers through public customization props:
<InertiaX prop="users" table={{ cellRenderers: [{ operation: 'register', key: 'workbench/role', value: WorkbenchRoleCell }], filterInputs: [ { operation: 'register', key: 'workbench/role-filter', value: WorkbenchRoleFilterInput, }, ], }}/>The input component receives the Filter definition, selected Clause, current JSON value, an
accessible label, and onChange. It should emit only protocol-valid JSON values or undefined to
clear the value.
Pick the registration scope
Section titled “Pick the registration scope”Component registration is local to that Table. Put the same operations on InertiaXProvider when
the type is an application-wide convention. Resolution is built-in → application → component.
register and replace are distinct; duplicates and missing replacement targets fail contextually.
Namespaced icon identifiers use the same catalog pattern through iconRenderers. A Column can emit
an icon name in its cell payload, and React resolves it without coupling Laravel to a React icon
library.
Diagnostics and failures
Section titled “Diagnostics and failures”Missing namespaced leaf renderers use the configured fallback policy and emit
INERTIAX_CATALOG_FALLBACK_USED with component, key, purpose, and source. Invalid top-level
components, schemas, catalog operations, callbacks, source capabilities, and pipeline outputs fail
rather than being swallowed.
Set runtimeOptions={{ diagnostics: { fallback: 'error' } }} on the provider when every custom
leaf registration is required. Use runtime inspection for resolution metadata; implementation functions remain
encapsulated. The direct renderer lab shows both a registered acme/money cell and an isolated
sibling that deliberately falls back.
Envelope pipelines
Section titled “Envelope pipelines”Schema pipelines are an advanced compatibility boundary for validated envelope transformations. They are not a replacement for a custom cell, a query callback, or a component region. Add a pipeline only when an integration must transform an envelope at a named, inspectable scope while preserving a valid protocol result.