Filtering
Filters let users narrow an Inertia Table by any column. Each filter type (text, numeric, set, date, boolean) exposes the right operators (contains, between, in, before/after) and persists its state in the URL so filtered views may be shared, bookmarked, and reloaded.
Types of Filters
Inertia Table ships with the following filter types:
TextFilterNumericFilterSetFilterDateFilterBooleanFilter
Adding Filters
To add filters to your table, use the make method on the filter class and pass the attribute of the Eloquent model you want to filter. Add the filter to the array returned by the filters() method in your Table class.
TextFilter::make('name');Inertia Table automatically generates a title-cased header based on the attribute name. You may customize the header by passing it as the second argument.
TextFilter::make('name', 'Full Name');Clauses
Each filter has one or more clauses that define how it is applied. Clauses are defined in the Clause enum, and each filter type ships with its own default set. To customize the available clauses, pass an array to the clauses argument or call the clauses() method.
use InertiaUI\Table\Filters\Clause;
TextFilter::make('name')->clauses([
Clause::Equals,
Clause::StartsWith,
Clause::Contains,
]);Nullable Attributes
To filter nullable attributes, pass the nullable argument to add the IsSet and IsNotSet clauses. Alternatively, you may use the nullable() method:
TextFilter::make('name', nullable: true);
TextFilter::make('name')->nullable();Validating and Modifying Input
By default, incoming filter input is validated to be filled and of the correct type. To customize validation, pass a closure to the validateUsing argument or call the validateUsing() method:
TextFilter::make('name')->validateUsing(function (mixed $value) {
if(is_string($value) && strlen($value) > 3) {
return $value;
}
});The callback should return the value itself when validation passes, not a boolean. This lets you modify the value before it is used in the query.
The callback accepts two additional parameters: the clause and the Eloquent builder:
use Illuminate\Database\Eloquent\Builder;
use InertiaUI\Table\Filters\Clause;
TextFilter::make('name')->validateUsing(function (mixed $value, Clause $clause, Builder $query) {
//
});Customizing the Query
Each combination of filter and clause has a specific way of applying itself to the query. To customize this, pass a closure to the applyUsing argument or call the applyUsing() method:
use Illuminate\Database\Eloquent\Builder;
use InertiaUI\Table\Filters\Clause;
TextFilter::make('name')->applyUsing(function (Builder $resource, string $attribute, Clause $clause, mixed $value) {
if($clause === Clause::Equals) {
$resource->where($attribute, $value);
}
});Passing an extensive closure to the static make() method may feel verbose. In such cases, you may pass an invokable class instead:
TextFilter::make('name', applyUsing: new MyCustomTextFilter);Preventing the Query From Being Wrapped
By default, each filter is wrapped in a where() clause to prevent conflicts with other filters. This is desirable in most cases, but sometimes you need direct access to the Query Builder instance, for example to remove a global scope. Pass the applyUnwrapped argument to the filter, or call the applyUnwrapped() method:
SetFilter::make('status', applyUsing: new StatusFilter, applyUnwrapped: true);Alternatively, the applyUsing() method accepts a second argument. Set it to true to prevent the query from being wrapped:
SetFilter::make('status')->applyUsing(function () {
//
}, true);Set Filter
The SetFilter has an options() method to populate the select options. Pass it an array of options:
SetFilter::make('status')->options([
'active' => 'Active',
'inactive' => 'Inactive',
]);To allow multiple selections, pass the multiple argument to the filter or call the multiple() method:
SetFilter::make('tags')->multiple();Set Filter Clauses
By default, the SetFilter has four default clauses to choose from:
InNotInEqualsNotEquals
To hide the clause selector in the frontend, use the withoutClause() method. The filter then always uses the Equals clause, which is useful for filtering by status.
SetFilter::make('status')->withoutClause();Filtering by Relationships
The SetFilter is well suited for filtering by relationships. The pluckOptionsFromModel helper populates the options from an Eloquent model:
SetFilter::make('company.id', 'Company')->pluckOptionsFromModel(Company::class, 'name');Alternatively, use the relationship name as an attribute (without .id) and call pluckOptionsFromRelation to automatically resolve the key and populate options from the related model:
SetFilter::make('company')->pluckOptionsFromRelation('name');Lazy Filter Options v4
By default, SetFilter options are serialized into the initial page props. For filters with a large option set (e.g. a list of companies or tags), you may defer loading until the filter popover is first opened by calling lazy().
SetFilter::make('company')
->pluckOptionsFromModel(Company::class)
->lazy();With lazy() enabled, the options are fetched via an Inertia partial reload the first time the user opens that filter's popover. Subsequent opens reuse the already-loaded options without an additional request.
lazy() may be combined with any other SetFilter options:
SetFilter::make('company')
->pluckOptionsFromModel(Company::class, 'name')
->multiple()
->withoutClause()
->lazy();Date Filter v4
The DateFilter provides a built-in calendar picker. It supports single date and date range selection (based on the active clause), and comes with several options to customize its behavior.
Time Selection
Enable time selection alongside dates with the withTime() method. By default it uses a 12-hour format. Pass true for 24-hour:
DateFilter::make('created_at')->withTime();
DateFilter::make('created_at')->withTime(time24h: true);Presets
Add shortcut buttons to the date picker for common date ranges:
DateFilter::make('created_at')->presets([
'Today' => now(),
'Yesterday' => now()->subDay(),
'Last 7 days' => [now()->subDays(7), now()],
'This month' => [now()->startOfMonth(), now()->endOfMonth()],
'This year' => [now()->startOfYear(), now()->endOfYear()],
]);Presets accept Carbon instances, date strings, or arrays of two values for ranges.
Timezone
By default, dates are compared as-is against the database. For databases storing timestamps in UTC while users live in different timezones, dates near midnight may not match as expected. Use timezone() to interpret the selected dates in a specific timezone:
DateFilter::make('created_at')->timezone(new \DateTimeZone('America/New_York'));To automatically use the browser's timezone, use browserTimezone(). The client sends its timezone via Intl.DateTimeFormat() and the filter reads it from the request:
DateFilter::make('created_at')->browserTimezone();Min and Max Dates
Constrain the selectable date range:
DateFilter::make('created_at')
->minDate('2020-01-01')
->maxDate('2030-12-31');Multiple Months
Show two (or more) months side-by-side in the calendar, which is helpful for range selection:
DateFilter::make('created_at')->numberOfMonths(2);Week Numbers
Display ISO week numbers in a column to the left of the calendar:
DateFilter::make('created_at')->showWeekNumbers();Week Start Day
Set which day the week starts on (0 = Sunday, 1 = Monday, ..., 6 = Saturday):
DateFilter::make('created_at')->weekStartsOn(0); // SundayClearable
Allow the user to clear the selected date from within the calendar:
DateFilter::make('created_at')->clearable();Combining Options
All options may be combined:
DateFilter::make('created_at')
->withTime(time24h: true)
->browserTimezone()
->minDate('2020-01-01')
->maxDate('2030-12-31')
->numberOfMonths(2)
->showWeekNumbers()
->clearable()
->presets([
'Today' => now(),
'This week' => [now()->startOfWeek(), now()->endOfWeek()],
'This month' => [now()->startOfMonth(), now()->endOfMonth()],
'This year' => [now()->startOfYear(), now()->endOfYear()],
]);Default Filters
You may enable a filter by default with a specific value and clause using the default() method. The clause argument is optional.
TextFilter::make('company')->default('GmbH');
DateFilter::make('created_at')->default(now()->subDays(7), Clause::GreaterThan);
NumericFilter::make('score')->default([55, 100], Clause::Between);Without a clause, the filter falls back to the first clause in the list.
Clause Symbols
In the filter button, the clause is displayed as a symbol. For example, the Does Not Equal clause is displayed as !=. To customize these symbols, import setClauseSymbols() from the @inertiaui/table package and pass it an object with the clauses you want to override. You may do this globally in your app.js file:
import { setClauseSymbols } from '@inertiaui/table-vue'
setClauseSymbols({
contains: '~',
greater_than: '>>',
// ...
})import { setClauseSymbols } from '@inertiaui/table-react'
setClauseSymbols({
contains: '~',
greater_than: '>>',
// ...
})Available Clauses
See the source code for the full list of available clauses.
Custom Filter Components v4
You may replace the default filter input UI for any filter via a slot (Vue) or render prop (React). The slot/prop name follows the pattern filter(attribute).
The slot scope provides:
| Prop | Description |
|---|---|
filter | The filter definition from the resource (includes options, label, etc.) |
value | The current filter state object (with value, clause, enabled properties) |
update | A callback to update the filter value (call it with the new value) |
setDisplayValue | Override the text shown in the filter button (e.g., setDisplayValue('Administrators')) |
table | The table instance from useTable() |
<Table :resource="users">
<template #filter(status)="{ filter, value, update, setDisplayValue }">
<MyCustomSelect
:options="filter.options"
:value="value.value"
@change="(v) => {
update(v)
setDisplayValue(v === 'active' ? 'Active Users' : 'Inactive Users')
}"
/>
</template>
</Table><Table
resource={users}
filter={{
status: ({ filter, value, update, setDisplayValue }) => (
<MyCustomSelect
options={filter.options}
value={value.value}
onChange={(v) => {
update(v)
setDisplayValue(v === 'active' ? 'Active Users' : 'Inactive Users')
}}
/>
),
}}
/>setDisplayValue is optional. Without it, the filter button derives its text from the value using the default formatting logic (option label lookup for set filters, raw value for others). Call it whenever you want the button to show something different from the stored value.
Reset All Filters v4
The "Add filter" dropdown automatically shows a "Clear all filters" option at the bottom whenever at least one filter is active. Clicking it disables all active filters at once.
You may also trigger this programmatically using the resetAllFilters() method exposed by useTable() or via a template ref:
<template #afterActions="{ table }">
<button @click="table.resetAllFilters()">Clear all filters</button>
</template><Table
resource={users}
afterActions={({ table }) => (
<button onClick={() => table.resetAllFilters()}>Clear all filters</button>
)}
/>Metadata
You may attach metadata to a filter via the meta argument on make() or the meta() method:
TextFilter::make('name')->meta([
'key' => 'value',
]);The default Table component ignores this metadata and does not pass it to any DOM element. It becomes useful when extending the Table component using slots, or when building a custom Table component.