List domain objects in tables driven by schemas, properties, and actions — with built-in filtering, sorting, and inline edit
Data tables in Core are driven by domain abstractions. An object schema provides column definitions and filter types. Actions provide row operations and bulk selections. The result is a table that stays in sync with your domain model — no duplication of labels, validation rules, or permissions.For the full API covering all column types, filter types, exports, traits, and pagination, see the Data Tables reference.
Direct column (StringProperty, NumberProperty, etc.)
Yes
ORDER BY column
from('relation.column')
Yes
Relationship join via PowerJoins
Aggregate (sumOf, countOf, etc.)
Yes
ORDER BY the aggregate column
computed()
No
Value derived in PHP, no DB column
ObjectProperty without sortBy
No
No single column to sort by
ObjectProperty with sortBy
Yes
Custom column or join
ObjectProperty columns are not sortable by default because there is no single obvious column to order by. Declare a sort target with ->sortBy() directly on the property in the schema:
Copy
ObjectProperty::make('customer', __('Customer')) ->sortBy('customer.name'), // sort by the related model's name via a join
Or sort by the foreign key for a simpler, index-friendly sort:
PropertyFilter::fromSchema() selects the right filter type based on the property’s metadata — no configuration needed:
Property characteristic
Filter rendered
Has options()
Set filter (checkbox list, enum labels)
Type boolean
Toggle filter
Type date
Date range filter
Type number / money
Numeric range filter
Everything else
Text search filter
Set a Lucide icon on the property with ->icon('Calendar') (PascalCase name). It is passed to the filter UI as metadata. For ObjectProperty filters, a property-level icon overrides the related object’s default filter icon.
Copy
public function filters(): array{ return [ PropertyFilter::fromSchema(Order::class, 'status'), // CoreSetFilter with OrderStatus options PropertyFilter::fromSchema(Order::class, 'ordered_at'), // date range ];}
When a table is constrained to a parent model, pass the parent as a constructor parameter and mark it with #[Remember] from InertiaUI\Table\Remember:
Copy
use InertiaUI\Table\Remember;class SchoolUserTable extends CoreTable{ public function __construct( #[Remember] public ?School $school = null, ) {} public function resource(): Builder|string { return SchoolUser::query() ->when($this->school, fn ($q) => $q->where('school_id', $this->school->id)); }}
Always mark scoped constructor parameters with #[Remember]. Exports and bulk actions are separate HTTP requests that reconstruct the table from scratch — without this attribute, those requests ignore the scope and run against the full dataset.
Register actions with coreActions(). They carry their own labels, icons, authorization, and confirmation dialogs — and work in row actions, bulk action bars, and dropdowns without extra configuration.
Copy
public function coreActions(): array{ return [ // Navigate to the detail page LinkCoreAction::make(__('View')) ->href(fn(Order $order) => route('orders.show', $order->id)) ->icon('Eye'), // Open an edit modal ModalLinkCoreAction::make(__('Edit')) ->url(fn(Order $order) => route('orders.edit', $order->id)) ->icon('Pencil'), // Bulk-capable action using WithTableAction CancelOrderAction::make(), ];}
Row actions automatically receive { id: row.id }. Bulk actions receive { ids: [...] }, or { ids: ['*'] } when the user selects all rows.See Table Actions for how to build actions that operate on single or bulk rows.
Add ->editable() to a PropertyColumn to enable inline editing directly in the table cell. Clicking the cell value opens a popover with the property’s input component: