Installation
composer require timolake/livewire-tables
Publish the config (optional):
php artisan vendor:publish --provider="Timolake\LivewireTables\LivewireTablesServiceProvider"
Basic Usage
Create a Livewire component that extends Timolake\LivewireTables\LivewireTable:
use Timolake\LivewireTables\LivewireTable;
class UserTable extends LivewireTable
{
public function configure(): void
{
$this->setPrimaryKey('id');
$this->setSearch(['name', 'email']);
$this->setColumns([
'id',
'name',
'email',
]);
}
public function query(): \Illuminate\Database\Eloquent\Builder
{
return User::query();
}
}
Render the Table
<livewire:user-table />
The package auto-generates pagination, search, and sorting UI.
LivewireTable as a base class and define configure() + query().setColumns().
$this->setColumns(['id', 'name', 'email']);
getColumnContent():
public function getNameContent($row): string
{
return "<strong>{$row->name}</strong>"; // HTML allowed
}
$this->addActionColumn([
'view' => fn($row) => view('livewire-tables::buttons.view', ['row' => $row]),
'edit' => fn($row) => view('livewire-tables::buttons.edit', ['row' => $row]),
]);
setSearch().addFilter():
$this->addFilter('active', function ($query, $value) {
return $query->where('is_active', $value);
});
Render filter UI in Blade:
<livewire:user-table :filters="['active' => true]" />
setBulkActions():
$this->setBulkActions([
'delete' => fn($rows) => User::whereIn('id', $rows)->delete(),
]);
mountTable() for initialization logic:
public function mountTable(): void
{
$this->setPerPage(20); // Override default
}
updatedSearch() or updatedSort() for custom logic.php artisan vendor:publish --tag=livewire-tables-views
public/css/livewire-tables.css.Eager Loading
with() in query():
public function query(): Builder
{
return User::with('posts')->query();
}
Search Scope Conflicts
setSearch() fields conflict with existing query scopes, override applySearch():
protected function applySearch(): void
{
$this->query()->where('name', 'like', "%{$this->search}%");
}
Pagination Reset
setPerPage() or filters does not reset pagination by default.$this->resetPage();
CSRF on Bulk Actions
@csrf to Blade or use @this directive:
<form wire:submit.prevent="deleteSelected" @this>
@csrf
<!-- ... -->
</form>
\DB::enableQueryLog();
$this->query()->get(); // Inspect with dd(\DB::getQueryLog());
wire:debug to inspect Livewire events:
@wire:debug
<livewire:user-table />
setSearch()/setSort() inputs to avoid SQL injection:
$this->search = strtolower($this->search);
Custom Table Classes
LivewireTable for reusable table logic:
class BaseTable extends LivewireTable
{
public function configure(): void
{
$this->setPerPage(15);
$this->setSearch(['name']);
}
}
Dynamic Columns
$this->setColumns(config('table.columns'));
Export Functionality
spatie/laravel-excel:
public function export()
{
return Excel::download(new UserExport, 'users.xlsx');
}
<button wire:click="export">Export</button>
Server-Side Processing
getData() to fetch only visible rows:
public function getData(): array
{
return $this->query()
->skip($this->offset())
->take($this->perPage())
->get()
->toArray();
}
config/livewire-tables.php:
'per_page' => 10,
setPerPage(0) or override hasPagination():
public function hasPagination(): bool { return false; }
getSortIcon():
protected function getSortIcon(string $field): string
{
return $this->sort === $field ? '↑↓' : '→';
}
How can I help you explore Laravel packages today?