Search Functionality
Laravel Livewire Tables provides global search that queries across all searchable columns.
Enable Search
Mark columns as searchable:
use Livewire\Tables\Columns\ TextColumn ;
public function columns () : array
{
return [
TextColumn :: make ( 'name' ) -> sortable () -> searchable (),
TextColumn :: make ( 'email' ) -> sortable () -> searchable (),
TextColumn :: make ( 'description' ) -> searchable (),
];
}
The global search input will query all three columns using LIKE queries combined with OR.
Search Debounce
Control the delay before search executes:
public function configure () : void
{
$this -> setSearchDebounce ( 500 ); // 500ms delay
}
Valid range: 0-5000 milliseconds. Default: 300ms.
Higher debounce values reduce server load by preventing searches on every keystroke.
Search on Specific Fields
For joined tables or aliases, specify the actual database column:
public function query () : Builder
{
return Product :: query ()
-> join ( 'brands' , 'products.brand_id' , '=' , 'brands.id' )
-> select ( 'products.*' , 'brands.name as brand_name' );
}
public function columns () : array
{
return [
TextColumn :: make ( 'name' ) -> searchable (),
TextColumn :: make ( 'brand_name' )
-> label ( 'Brand' )
-> searchable ( 'brands.name' ), // Search on actual column
];
}
Custom Search Logic
Use a closure for complex search requirements:
use Livewire\Tables\Columns\ BladeColumn ;
use Illuminate\Database\Eloquent\ Builder ;
BladeColumn :: make ()
-> label ( 'Full Name' )
-> searchable ( fn ( Builder $query , string $search ) => $query
-> orWhere ( 'first_name' , 'LIKE' , "%{ $search }%" )
-> orWhere ( 'last_name' , 'LIKE' , "%{ $search }%" )
)
-> render ( fn ( $row ) => $row -> first_name . ' ' . $row -> last_name );
Custom search closures are only supported on BladeColumn. Use searchable('field.name') for other column types.
Search State
Access the current search query in your component:
public string $search = '' ;
public function updatedSearch () : void
{
$this -> deselectAll ();
$this -> resetPage ();
$this -> dispatchFiltersChanged ();
}
Clear search programmatically:
Check if search is active:
if ( $this -> hasSearch ()) {
// Search is active
}
Sorting
Laravel Livewire Tables supports single and multi-column sorting.
Enable Sorting
Mark columns as sortable:
public function columns () : array
{
return [
TextColumn :: make ( 'name' ) -> sortable (),
TextColumn :: make ( 'email' ) -> sortable (),
TextColumn :: make ( 'created_at' ) -> sortable (),
];
}
Users can click column headers to sort.
Single-Column Sorting
Click a sortable column header:
First Click
Sorts by the default direction (ascending by default).
Second Click
Reverses the sort direction.
Third Click
Clears the sort.
Multi-Column Sorting
Hold Shift and click additional column headers to sort by multiple columns:
1. Click "Name" → sorts by name asc
2. Shift+click "Created" → sorts by name asc, created_at asc
3. Shift+click "Email" → sorts by name asc, created_at asc, email asc
Each sorted column displays:
Sort direction indicator (↑ or ↓)
Sort order number when multiple columns are sorted
Multi-column sorting respects the order in which columns were clicked.
Default Sort Direction
Set the initial sort direction:
public function configure () : void
{
$this -> setDefaultSortDirection ( 'desc' );
}
Options: 'asc' or 'desc'. Default: 'asc'.
This affects the first click on any sortable column.
Sort State
The component tracks sort state in $sortFields:
public array $sortFields = [];
// ['name' => 'asc', 'created_at' => 'desc']
Programmatic sorting:
$this -> sortBy ( 'name' ); // Toggle sort on 'name'
$this -> clearSort (); // Clear all sorting
$this -> clearSortField ( 'name' ); // Clear specific field
Check sort state:
if ( $this -> isSortedBy ( 'name' )) {
$direction = $this -> getSortDirection ( 'name' ); // 'asc' or 'desc'
$order = $this -> getSortOrder ( 'name' ); // 1, 2, 3, etc.
}
Sort Chips
Active sorts are displayed as chips in the toolbar:
Users can remove individual sorts by clicking the ❌ button.
Sorting on Joined Tables
Sort by joined columns using dot notation:
public function query () : Builder
{
return Order :: query ()
-> join ( 'brands' , 'orders.brand_id' , '=' , 'brands.id' )
-> select (
'orders.*' ,
'brands.name as brands_name'
);
}
public function columns () : array
{
return [
TextColumn :: make ( 'orders.id' ) -> label ( '#' ) -> sortable (),
TextColumn :: make ( 'customer_name' ) -> sortable (),
TextColumn :: make ( 'brands.name' ) -> label ( 'Brand' ) -> sortable (),
];
}
When sorting on joined columns, ensure the column is included in the SELECT clause or use the full table.column syntax.
The Search Pipeline
Understanding how search works internally:
User Input
User types in the search box.
Debounce
After the configured delay, Livewire updates $search.
SearchStep
The SearchStep applies LIKE queries to all searchable columns: $query -> where ( function ( Builder $q ) use ( $search ) {
foreach ( $searchableColumns as $column ) {
$q -> orWhere ( $column -> field (), 'LIKE' , "%{ $search }%" );
}
});
Results
The filtered query is passed to the next pipeline step.
The Sort Pipeline
Understanding how sorting works internally:
User Click
User clicks a sortable column header (with or without Shift).
Update State
The sortBy() method updates $sortFields: public function sortBy ( string $field ) : void
{
if ( array_key_exists ( $field , $this -> sortFields )) {
if ( $this -> sortFields [ $field ] === 'asc' ) {
$this -> sortFields [ $field ] = 'desc' ;
} else {
unset ( $this -> sortFields [ $field ]);
}
} else {
$this -> sortFields [ $field ] = $this -> defaultSortDirection ?? 'asc' ;
}
$this -> resetPage ();
}
SortStep
The SortStep applies ORDER BY clauses in order: foreach ( $sortFields as $field => $direction ) {
$query -> orderBy ( $field , $direction );
}
Results
The sorted query is passed to pagination.
Complete Example
app/Livewire/OrdersTable.php
<? php
namespace App\Livewire ;
use App\Models\ Order ;
use Illuminate\Database\Eloquent\ Builder ;
use Livewire\Tables\Columns\ TextColumn ;
use Livewire\Tables\Columns\ DateColumn ;
use Livewire\Tables\Livewire\ DataTableComponent ;
class OrdersTable extends DataTableComponent
{
protected string $tableKey = 'orders' ;
public function configure () : void
{
$this -> setDefaultPerPage ( 25 );
$this -> setSearchDebounce ( 300 );
$this -> setDefaultSortDirection ( 'desc' );
$this -> setEmptyMessage ( 'No orders found.' );
}
public function query () : Builder
{
return Order :: query ()
-> join ( 'brands' , 'orders.brand_id' , '=' , 'brands.id' )
-> select (
'orders.*' ,
'orders.id as orders_id' ,
'brands.name as brands_name' ,
'brands.country as brands_country'
);
}
public function columns () : array
{
return [
TextColumn :: make ( 'orders.id' )
-> label ( '#' )
-> sortable (),
TextColumn :: make ( 'customer_name' )
-> label ( 'Customer' )
-> sortable ()
-> searchable (),
TextColumn :: make ( 'customer_email' )
-> label ( 'Email' )
-> sortable ()
-> searchable (),
TextColumn :: make ( 'product_name' )
-> label ( 'Product' )
-> sortable ()
-> searchable (),
TextColumn :: make ( 'brands.name' )
-> label ( 'Brand' )
-> sortable ()
-> searchable (),
TextColumn :: make ( 'brands.country' )
-> label ( 'Country' )
-> sortable ()
-> searchable (),
TextColumn :: make ( 'quantity' )
-> label ( 'Qty' )
-> sortable ()
-> headerClass ( 'text-right' )
-> cellClass ( 'text-right' ),
TextColumn :: make ( 'unit_price' )
-> label ( 'Unit Price' )
-> sortable ()
-> headerClass ( 'text-right' )
-> cellClass ( 'text-right font-semibold' )
-> format ( fn ( $value ) => '$' . number_format ( $value , 2 )),
TextColumn :: make ( 'status' )
-> label ( 'Status' )
-> sortable (),
DateColumn :: make ( 'ordered_at' )
-> label ( 'Date' )
-> sortable ()
-> format ( 'M d, Y' ),
];
}
}
Add database indexes to columns that are frequently searched: Schema :: table ( 'products' , function ( Blueprint $table ) {
$table -> index ( 'name' );
$table -> index ( 'email' );
});
For large datasets, consider MySQL full-text indexes: Schema :: table ( 'products' , function ( Blueprint $table ) {
$table -> fullText ([ 'name' , 'description' ]);
});
Then customize search: TextFilter :: make ( 'search' )
-> filter ( fn ( Builder $query , mixed $value ) =>
$query -> whereFullText ([ 'name' , 'description' ], $value )
);
Only mark essential columns as searchable to reduce query complexity: // Good: 3-5 searchable columns
TextColumn :: make ( 'name' ) -> searchable (),
TextColumn :: make ( 'email' ) -> searchable (),
// Avoid: 10+ searchable columns
Increase Debounce for Large Tables
Higher debounce reduces query load: $this -> setSearchDebounce ( 500 ); // or even 750ms
Next Steps
Pagination Customize pagination behavior
Filters Add filters to narrow down your data