Overview
Filters allow users to narrow down table data using various input types. All filters extend the base Filter class and implement FilterContract.
Defining Filters
Override the filters() method in your table component:
use Livewire\Tables\Filters\TextFilter;
use Livewire\Tables\Filters\SelectFilter;
use Livewire\Tables\Filters\BooleanFilter;
public function filters(): array
{
return [
TextFilter::make('name')
->label('Name')
->placeholder('Search by name...'),
SelectFilter::make('status')
->label('Status')
->setOptions([
'active' => 'Active',
'inactive' => 'Inactive',
]),
BooleanFilter::make('verified')
->label('Verified Only'),
];
}
Filter Types
TextFilter
Applies a LIKE query on the specified field:
TextFilter::make('email')
->label('Email')
->placeholder('Filter by email...');
Custom Logic
Search across multiple fields:
TextFilter::make('full_name')
->label('Name')
->filter(fn(Builder $query, mixed $value) => $query
->where('first_name', 'LIKE', "%{$value}%")
->orWhere('last_name', 'LIKE', "%{$value}%")
);
SelectFilter
Dropdown with predefined options:
SelectFilter::make('role')
->label('Role')
->setOptions([
'admin' => 'Admin',
'editor' => 'Editor',
'viewer' => 'Viewer',
]);
Multiple Selection
Allow selecting multiple options:
SelectFilter::make('tags')
->label('Tags')
->setOptions($tagOptions)
->multiple()
->filter(fn(Builder $query, mixed $value) =>
$query->whereIn('tag_id', $value)
);
Searchable Dropdown
Renders an Alpine-powered dropdown with text search:
SelectFilter::make('country')
->label('Country')
->setOptions($countries)
->searchable();
Combine searchable() and multiple() for a searchable multi-select dropdown.
searchable() is not supported on filters with parent() dependency. Dependent filters use a native <select> element.
BooleanFilter
Simple true/false toggle:
BooleanFilter::make('active')
->label('Active Only');
Applies a WHERE field = 1 query when checked.
NumberFilter
Numeric input with optional bounds:
NumberFilter::make('price')
->label('Minimum Price')
->min(0)
->max(10000)
->step(0.01)
->filter(fn(Builder $query, mixed $value) =>
$query->where('price', '>=', $value)
);
NumberRangeFilter
Two inputs for min/max range:
NumberRangeFilter::make('price')
->label('Price Range')
->min(0.0)
->max(9999.99)
->step(0.01)
->filter(function(Builder $query, mixed $value): Builder {
if (isset($value['min']) && $value['min'] !== '') {
$query->where('price', '>=', (float) $value['min']);
}
if (isset($value['max']) && $value['max'] !== '') {
$query->where('price', '<=', (float) $value['max']);
}
return $query;
});
Values are automatically clamped to min/max bounds.
DateFilter
Single date selection:
DateFilter::make('created_at')
->label('Created On')
->minDate('2020-01-01')
->maxDate('2030-12-31')
->filter(fn(Builder $query, mixed $value) =>
$query->whereDate('created_at', $value)
);
DateRangeFilter
From/to date range using Flatpickr:
DateRangeFilter::make('created_at')
->label('Created Between')
->format('Y-m-d')
->minDate('2020-01-01')
->maxDate('2030-12-31')
->filter(function(Builder $query, mixed $value): Builder {
if (isset($value['from']) && $value['from'] !== '') {
$query->whereDate('created_at', '>=', $value['from']);
}
if (isset($value['to']) && $value['to'] !== '') {
$query->whereDate('created_at', '<=', $value['to']);
}
return $query;
});
MultiDateFilter
Select multiple individual dates using Flatpickr:
MultiDateFilter::make('event_dates')
->label('Event Dates')
->format('Y-m-d')
->minDate('2024-01-01')
->maxDate('2025-12-31')
->filter(function(Builder $query, mixed $value): Builder {
return $query->where(function(Builder $q) use ($value): void {
foreach ($value as $date) {
$q->orWhereDate('event_date', $date);
}
});
});
Dependent Filters
Create cascading filters where the child’s options depend on the parent’s value:
public function filters(): array
{
return [
SelectFilter::make('country')
->label('Country')
->setOptions([
'us' => 'United States',
'ca' => 'Canada',
'mx' => 'Mexico',
]),
SelectFilter::make('city')
->label('City')
->parent('country')
->parentFilter(fn($countryId) =>
City::where('country_id', $countryId)
->pluck('name', 'id')
->toArray()
),
];
}
How It Works
Set Dependency
Use parent('filter_key') to link the child to the parent filter.
Define Options Callback
Use parentFilter(Closure) to receive the parent’s current value and return an options array.
Automatic Reset
When the parent value changes, the child filter automatically resets to empty.
Options Reload
The child <select> re-renders with new options from resolveOptions().
Multi-Level Dependencies
Chain multiple levels of dependent filters:
public function filters(): array
{
return [
SelectFilter::make('country')
->label('Country')
->setOptions($countries),
SelectFilter::make('state')
->label('State')
->parent('country')
->parentFilter(fn($countryId) =>
State::where('country_id', $countryId)
->pluck('name', 'id')
->toArray()
),
SelectFilter::make('city')
->label('City')
->parent('state')
->parentFilter(fn($stateId) =>
City::where('state_id', $stateId)
->pluck('name', 'id')
->toArray()
),
];
}
Dependent filters do not support searchable(). The native <select> element is used to ensure proper reactivity.
Custom Filter Logic
Use the filter() method to override default query behavior:
Complex Queries
SelectFilter::make('status')
->label('Status')
->setOptions(['recent' => 'Recent', 'old' => 'Old'])
->filter(fn(Builder $query, mixed $value) => match($value) {
'recent' => $query->where('created_at', '>=', now()->subDays(30)),
'old' => $query->where('created_at', '<', now()->subDays(30)),
default => $query,
});
Joined Tables
SelectFilter::make('brands.tier')
->label('Brand Tier')
->setOptions([
'' => 'All Tiers',
'premium' => 'Premium',
'standard' => 'Standard',
'budget' => 'Budget',
])
->filter(fn(Builder $query, mixed $value) =>
$query->where('brands.tier', $value)
);
Initial Values
Set a default initial value that’s pre-applied on load:
SelectFilter::make('status')
->label('Status')
->setOptions([
'active' => 'Active',
'inactive' => 'Inactive',
])
->initialValue('active');
For multiple selection:
SelectFilter::make('status_multi')
->label('Statuses')
->multiple()
->searchable()
->setOptions([
'pending' => 'Pending',
'shipped' => 'Shipped',
'delivered' => 'Delivered',
])
->initialValue(['pending', 'shipped']);
Initial values are only applied when the table first mounts and no cached filter state exists.
Filter Keys
Override the internal key:
SelectFilter::make('category')
->key('cat')
->label('Category')
->setOptions($categories);
Useful for shorter URL query parameters or avoiding naming conflicts.
Styling Filters
Customize filter appearance:
TextFilter::make('name')
->label('Product Name')
->filterClass('filter-name') // Wrapper class
->labelClass('text-indigo-700 font-semibold') // Label class
->inputClass('border-indigo-300 focus:ring-indigo-500'); // Input class
Or use the groupClass() alias:
TextFilter::make('name')
->groupClass('mb-4 border-b pb-3');
Common Methods
| Method | Description |
|---|
label(string) | Display label |
key(string) | Override the internal key |
placeholder(string) | Placeholder text |
default(mixed) | Default value (not pre-applied) |
initialValue(mixed) | Pre-applied initial value |
filter(Closure) | Custom query callback |
groupClass(string) | CSS class for filter wrapper |
labelClass(string) | CSS class for label |
inputClass(string) | CSS class for input element |
filterClass(string) | Alias for groupClass() |
Active Filters
Active filters are displayed as chips in the toolbar:
Users can:
- Remove individual filters by clicking the ❌ button
- Clear all filters at once
Programmatic Access
Get applied filters in your component:
$applied = $this->getAppliedFilters();
// ['status' => 'active', 'price' => ['min' => 100, 'max' => 500]]
Check if filters are active:
if ($this->hasActiveFilters()) {
// Do something
}
Complete Example
app/Livewire/ProductsTable.php
<?php
namespace App\Livewire;
use App\Models\Product;
use Illuminate\Database\Eloquent\Builder;
use Livewire\Tables\Filters\TextFilter;
use Livewire\Tables\Filters\SelectFilter;
use Livewire\Tables\Filters\BooleanFilter;
use Livewire\Tables\Filters\NumberRangeFilter;
use Livewire\Tables\Filters\DateRangeFilter;
use Livewire\Tables\Livewire\DataTableComponent;
class ProductsTable extends DataTableComponent
{
public function query(): Builder
{
return Product::query();
}
public function filters(): array
{
return [
TextFilter::make('name')
->label('Product Name')
->placeholder('Filter by name...')
->filter(fn(Builder $query, mixed $value) =>
$query->where('name', 'LIKE', "%{$value}%")
),
SelectFilter::make('category')
->key('cat')
->label('Category')
->searchable()
->setOptions([
'' => 'All Categories',
'Laptops' => 'Laptops',
'Phones' => 'Phones',
'Tablets' => 'Tablets',
])
->filter(fn(Builder $query, mixed $value) =>
$query->where('category', $value)
),
SelectFilter::make('subcategory')
->key('subcat')
->label('Subcategory')
->parent('cat')
->parentFilter(function(mixed $value): array {
$map = [
'Laptops' => ['Business' => 'Business', 'Gaming' => 'Gaming'],
'Phones' => ['Flagship' => 'Flagship', 'Mid-range' => 'Mid-range'],
'Tablets' => ['iPad' => 'iPad', 'Android' => 'Android'],
];
return $map[$value] ?? [];
})
->filter(fn(Builder $query, mixed $value) =>
$query->where('subcategory', $value)
),
BooleanFilter::make('active')
->label('Active Only')
->filter(fn(Builder $query, mixed $value) =>
$query->where('active', (bool) $value)
),
NumberRangeFilter::make('price')
->label('Price Range')
->min(0.0)
->max(9999.99)
->step(0.01)
->filter(function(Builder $query, mixed $value): Builder {
if (isset($value['min']) && $value['min'] !== '') {
$query->where('price', '>=', (float) $value['min']);
}
if (isset($value['max']) && $value['max'] !== '') {
$query->where('price', '<=', (float) $value['max']);
}
return $query;
}),
DateRangeFilter::make('release_date')
->label('Release Date Range')
->format('Y-m-d')
->minDate('2020-01-01')
->maxDate('2027-12-31')
->filter(function(Builder $query, mixed $value): Builder {
if (isset($value['from']) && $value['from'] !== '') {
$query->whereDate('release_date', '>=', $value['from']);
}
if (isset($value['to']) && $value['to'] !== '') {
$query->whereDate('release_date', '<=', $value['to']);
}
return $query;
}),
];
}
}
Next Steps
Sorting & Search
Configure search and multi-column sorting
Pagination
Customize pagination behavior