Column API
Column classes live in InertiaX\Table\Columns. Create a builder with
ColumnClass::make(string $key, ?string $label = null).
Common options
Section titled “Common options”key(string $key)Link to key- Set the source field key
label(?string $label)Link to label- Set the heading; an omitted label is derived from the key
relationship(string $relation, string $attribute)Link to relationship- Read a persisted scalar field through a single Eloquent
BelongsTorelation type(ColumnType|Link to typestring $type) - Set a built-in type or namespaced custom type
sortable(bool $value = true)Link to sortable- Permit sorting
searchable(bool $value = true)Link to searchable- Include in global search
filterable(bool $value = true)Link to filterable- Permit automatic filter generation
copyable(bool $value = true)Link to copyable- Show the copy control
toggleable(bool $value = true)Link to toggleable- Include in the column picker
visible(bool $value = true)Link to visible- Set initial visibility
wrap(bool $value = true)Link to wrap- Wrap displayed content
truncate(?int $length)Link to truncate- Limit displayed text length;
0disables truncation;nullorclearTruncate()removes the builder override filter($filter, ?string $label = null)Link to filter- Attach a filter object, builder, Filter class name, or key string; it queries this column and keeps its own key, or takes the column’s
meta(array|Link to metastring $meta, mixed $value = null) - Merge column metadata or set one metadata key
ColumnType lives in InertiaX\Table\Columns\Enums.
For defaults, see Defaults.
Clearing a truncation override restores inherited/default behavior; use truncate(0) to disable
truncation even when the table sets a length.
Hidden values are still sent to the browser; see Security.
Built-in value formats
Section titled “Built-in value formats”TextColumnLink to TextColumn- Text value
NumberColumnLink to NumberColumnnumberCast(string $cast)defaults tofloat; acceptsint,integer,real,float,double,decimalBooleanColumnLink to BooleanColumntrueLabel(string)/falseLabel(string)default toTrue/False;trueIcon(string)/falseIcon(string)set iconsDateColumnLink to DateColumnformat(string)defaults toY-m-dDateTimeColumnLink to DateTimeColumnformat(string)defaults toY-m-d H:i:sTimeColumnLink to TimeColumnformat(string)defaults toH:i:sJsonColumnLink to JsonColumn- Preserves structured JSON; React formats its display
BadgeColumnLink to BadgeColumnvariant(Closure|array|string|Variant $variant, mixed $default = null)andicon(Closure|array|string $icon, mixed $default = null)
Date, datetime, and time columns also support timezone(?string) and translate(bool = true).
The default timezone is the application timezone; translation is off by default and uses the
application locale when enabled. Formatting does not mutate a model-owned Carbon value.
Integer casts preserve large integers as decimal strings; decimal preserves a string value.
Invalid numeric values fail instead of becoming zero. Boolean labels and icons affect presentation;
the cell value remains Boolean.
The built-in renderer styles primary, secondary, success, danger, warning, and info
badge variants. These are also the cases in InertiaX\Table\Columns\Enums\Variant.
Other variant strings are accepted but need application styling or a custom renderer.
Number presentation
Section titled “Number presentation”NumberColumn::make('amount_cents', 'Amount') ->numberCast('int') ->currency('USD', divideBy: 100) ->locale('en-US');
NumberColumn::make('ratio')->numberCast('decimal')->decimals(3)->locale('fr-FR');currency(string $currency, int $divideBy = 1)Link to currency- Three uppercase letters, such as
USDorJPY;divideBymust be a power of ten from1through1000000000 decimals(int $digits)Link to decimals- Exactly
0through20fractional digits locale(string $locale)Link to locale- Two- or three-letter language, optional four-letter script, optional two-letter or three-digit region:
en,en-US,zh-Hans-CN,es-419
Calling any presentation method enables formatting. Without currency(), the style is decimal;
locale defaults to en-US and the divisor to 1. Without decimals(), currency precision follows
Intl.NumberFormat currency defaults; decimal formatting uses its default precision. Currency
codes are syntax-checked, not checked against a bundled currency registry. Locale matching is
case-insensitive; variants and extensions are unsupported. The renderer canonicalizes the locale
and rejects locales unavailable in its runtime instead of falling back to an unrelated locale.
Formatting changes the built-in cell text, title and copy text together. It leaves the number cast, raw cell value and query units unchanged. Integer/decimal strings are scaled exactly before formatting; an earlier float conversion can already have lost precision. Null displays as empty; negative zero keeps its sign. Non-numeric transformer or decorator output fails with table and column context. Per-row currencies, exchange conversion and locale-aware filter input need application code.
A NumberColumn subclass can declare protected nullable $currency, $locale, $divideBy and
$fractionDigits properties. Their defaults are null (unconfigured); inherited values apply
until a fluent method overrides that option. currency() overrides both currency and divisor.
Formatting alone enables no query capability. Label a filter for scaled cents Amount (cents),
since 8419 is still its input value for $84.19.
For stored cents, use ->numberCast('int')->currency('USD', divideBy: 100) without a transformer
that also divides by 100; combining them scales the amount twice. Use a transformer when you
intend to change the serialized value itself.
@inertiax/table-core exports formatTableNumber(value, format) for custom renderers; NumberFormat belongs to
@inertiax/table-protocol. To reuse one Intl.NumberFormat, call createTableNumberFormatter(format)
and retain its returned function within a component or request. Bound any multi-format cache.
Invalid format options and values throw; formatting does not negotiate the browser locale.
Related scalar fields
Section titled “Related scalar fields”TextColumn::make('customer_name', 'Customer') ->relationship('customer', 'name') ->searchable() ->sortable() ->filterable();Use relationship() with an Eloquent query source when a column represents one persisted field
on a related model. The example uses the order model’s customer() relation and the customer’s
name field. The column key stays customer_name in table state. Enable search, sorting and
filtering separately; the binding alone does not enable them.
The displayed value and built-in comparisons use the same related database field, with the
relation’s keys, ordinary where constraints and model scopes. That includes the comparisons of a
filter attached with filter() and of a filter whose column() names this column. Missing or
scoped-out related records produce null. PHP accessors, casts and withDefault() objects on the
related model do not change that value. Column transformers still receive the root record; use a
loaded relation and transformUsing() when you need accessor or default-object presentation.
Only standard, single-hop BelongsTo relations are supported. Custom foreign and owner keys and
same-table relations work. The table’s row key must remain a root field. Related fields must be
persisted scalars: JSON paths, computed properties and selected aliases require explicit queries
or callbacks. Ordinary root joins and explicit root projections are supported when the root table
keeps its original name and the selected fields include the row key. Root scopes must preserve
added projections: use addSelect() instead of replacing the selection with select().
The helper rejects collection/custom sources, aliased root sources, cross-connection relations, other relation families, and relation queries with ordering, limits, offsets, grouping, having, unions, joins, custom selects, distinct or locks. Same-table relations additionally require simple column constraints; raw SQL, nested condition groups and subqueries need an explicit query. These declaration errors fail before retrieving table rows.
sortUsing() and searchUsing() retain their explicit overrides. Custom clause applyUsing() and
filter applyClauseUsing() callbacks receive the column key as $column and own their query
behavior; the helper does not rewrite custom SQL.
Existing dotted keys such as customer.name retain their data-path behavior and do not declare a
query binding. For caller-owned projections, see custom search and sorting.
Relationship counts and sums
Section titled “Relationship counts and sums”NumberColumn::make('orders_count', 'Orders') ->count('orders') ->numberCast('int') ->sortable() ->filterable();
NumberColumn::make('paid_total', 'Paid total (cents)') ->sum('paidOrders', 'amount_cents') ->sortable() ->filterable();Use count() or sum() with an Eloquent query source and a standard, single-hop HasMany
relation. Here, paidOrders() can be an ordinary named relation such as
return $this->orders()->where('paid', true);. Custom relation keys, ordinary where constraints
and related model scopes apply to both the displayed aggregate and built-in comparisons.
Each parent keeps one row and its original row key.
An empty count is 0. A sum is null when there are no matching rows or all matching values are
null; it is not converted to zero. NumberColumn casts to float by default; the count example
chooses int. Sum fields must be persisted numeric database fields. Related PHP accessors and
casts do not change the aggregate.
The methods declare a value; enable sorting and filtering separately. Numeric aggregates do not
participate in default global text search. Sorting uses the aggregate, then the root row key for
ties; null placement follows the database. Built-in filters compare stored units: an
amount_cents sum of 300 matches an equals filter value of 300, even when currency presentation
displays $3.00. Use ->numberCast('int')->currency('USD', divideBy: 100) for stored cents;
null sums still display as empty.
The query restrictions above also apply to aggregates, including the
restrictions on same-table constraints and on relation ordering, limits and custom selects.
Nested, polymorphic, through and pivot relations need explicit queries. sortUsing(), custom
clause callbacks and applyClauseUsing() callbacks retain ownership of their queries. Existing
withCount() / withSum() projections remain usable through those callbacks; a key such as
orders_count alone never declares an aggregate or enables automatic comparison of a selected
alias. The last relationship(), count() or sum() call on a builder selects its binding.
Automatic columns
Section titled “Automatic columns”autoColumns() shows the fields the model serializes with toArray():
- the attributes each row carries: every column of the model’s table, or what a query selects,
including selects that global scopes add and aliases such as
withCount()counts; for a collection, the attributes its first model loaded; - then the model’s
$appends; - limited to
$visiblewhen it is set, in the order$visiblelists; - without
$hiddenfields and the fields passed toexcept.
It always adds the row key, as a hidden column that is not toggleable or filterable, even when
$visible leaves it out or $hidden or except names it. Appended accessors cannot be sorted, searched, or
filtered, and neither can a query’s aliases or columns joined from another table. A raw select
contributes only its final as alias, and another table’s * adds no field. When no field
remains besides the row key, autoColumns() throws a ColumnDefinitionException. Explicit
columns with the same key take precedence and appear after the inferred columns. Explicit
AutoColumn::make($key) infers only that field and retains its chained options.
autoColumns(except: ['internal_notes']) leaves fields out of one table; in a Table class, set
$defaultAutoColumnsExcept. autoColumns() without except keeps the list already set.
Inference needs an Eloquent model from the query or first collection row. Recognized model casts
win over schema types. The array cast infers JsonColumn with both autoColumns() and
AutoColumn::make(), preserving structured values, empty arrays, and nulls. An unrecognized cast
falls back to text; without a cast, schema inference runs, followed by timestamp-name heuristics
(_at), then text. Schema inference reads the database type, so a bigint unsigned column
becomes a number column. On MySQL and SQLite, Laravel’s boolean() creates a tinyint(1) column,
which becomes a boolean column and accepts only 0 and 1. On SQL Server it creates bit, which also
becomes a boolean column. A MySQL or PostgreSQL bit(n) column stays text. Schema inspection
failures are logged and use fallback inference. A query that selects * needs schema inspection to
list the table’s columns: if inspection fails, $visible takes their place, and autoColumns()
throws when $visible is empty.
Use explicit columns for parameterized or custom casts whose desired type is not inferred, and for plain array rows or empty collections. Review field exposure before using bulk inference.
Value transformers
Section titled “Value transformers”transformUsing($transformer) appends a transformer; repeated calls do not replace earlier stages.
mapAs(array $map, mixed $default = null) adds a mapping at priority 10.
addValueTransformer()Link to addValueTransformer- Parameters
$transformer, ?int $priority = null, ?string $name = null replaceValueTransformer()Link to replaceValueTransformer- Parameters
string $name, $transformer removeValueTransformer()Link to removeValueTransformer- Parameters
string $name clearValueTransformers()Link to clearValueTransformers- Parameters None; also removes built-in transformations
Transformers accept a closure, a ValueTransformer object, or its container-resolved class name.
The contract’s transform() receives the value, record, column, and table. Returning null
produces a null cell value. Transformations affect serialized cells, not query comparisons.
Lower priorities run first; the default priority is 100. Equal priorities retain declaration order. Replacing a named stage retains its priority and position. Duplicate names and missing replacement/removal targets fail.
Cell decorators
Section titled “Cell decorators”decorateUsing($decorator) appends a decorator. Parallel methods are addCellDecorator($decorator, ?int $priority = null, ?string $name = null), replaceCellDecorator(string $name, $decorator),
removeCellDecorator(string $name), and clearCellDecorators().
A closure or container-resolved CellDecorator receives the current CellPayload, record, column,
and table. It may mutate or replace the payload; returning null retains the current payload.
Ordering follows the same rules as transformers. Both contracts live in
InertiaX\Table\Columns\Contracts.
Column meta() applies to every row; decorator metadata belongs to one cell. See
Custom columns for an example.
Query callbacks
Section titled “Query callbacks”For Eloquent and collections, sortUsing(?Closure $callback, DataSourceKind|string ...$sources) and
searchUsing(?Closure $callback, DataSourceKind|string ...$sources) customize query behavior.
Searchable columns whose type is not text require searchUsing(), including a TextColumn
with a custom type.
supportsSortSources(...$sources) and supportsSearchSources(...$sources) set the supported kinds
separately. DataSourceKind is in InertiaX\Table\Enums.
See Custom query behavior for arguments and return contracts.
Custom sources reject these callbacks. See Custom-source search for their searchable-column requirements.
See Callback context for injectable arguments and resolution rules.