caseraph/laravel-livewire-tables
Installation
composer require caseraph/laravel-livewire-tables
Publish the config (if needed):
php artisan vendor:publish --provider="Caseraph\LivewireTables\LivewireTablesServiceProvider"
First Component Create a Livewire component:
php artisan make:livewire Admin/UsersTable
Update the component class (app/Http/Livewire/Admin/UsersTable.php):
use Caseraph\LivewireTables\LivewireTable;
class UsersTable extends LivewireTable
{
public function configure(): void
{
$this->setPrimaryKey('id');
$this->setSearch(['name', 'email']);
$this->setPerPage(10);
}
public function columns(): array
{
return [
'name' => 'Name',
'email' => 'Email',
'created_at' => 'Created At',
];
}
public function query(): Builder
{
return User::query();
}
}
First Blade View
<livewire:admin.users-table />
Replace a static Eloquent paginate() table with dynamic filtering, sorting, and pagination in under 10 minutes. Example:
// Replace this:
public function render()
{
return view('livewire.users', [
'users' => User::paginate(10),
]);
}
// With this:
public function render()
{
return view('livewire.users');
}
Define Data Source
Override query() to return a base Eloquent query:
public function query(): Builder
{
return User::with('roles')->where('active', true);
}
Configure Table Behavior
public function configure(): void
{
$this->setPrimaryKey('id');
$this->setSearch(['name', 'email', 'phone']);
$this->setPerPage(25);
$this->setDefaultSort('name', 'asc');
$this->setSearchDebounce(500); // ms
}
Customize Columns
public function columns(): array
{
return [
'name' => Column::make('Name')
->sortable()
->searchable(),
'email' => Column::make('Email')
->toggleable(isToggledByDefault: true),
'roles' => Column::make('Roles')
->format(fn ($value, $row) => $row->roles->pluck('name')->join(', ')),
'actions' => Column::make('Actions')
->view('livewire-tables::columns.actions', [
'edit' => route('users.edit', $row->id),
'delete' => route('users.destroy', $row->id),
]),
];
}
Add Actions
public function actions(): array
{
return [
'edit' => Action::make('Edit')
->icon('edit')
->url(fn ($row) => route('users.edit', $row->id)),
'delete' => Action::make('Delete')
->icon('trash')
->action(fn ($row) => $this->deleteUser($row)),
];
}
public function deleteUser(User $user): void
{
$user->delete();
$this->dispatch('user-deleted');
}
setApiEndpoint() to fetch data from a custom API:
public function configure(): void
{
$this->setApiEndpoint('/api/users');
}
public function query(): Builder
{
return User::with('department.projects');
}
public function columns(): array
{
return [
'department.projects' => Column::make('Projects')
->format(fn ($value) => $value->pluck('name')->join(', ')),
];
}
canSee() to hide/show columns/actions:
public function columns(): array
{
return [
'email' => Column::make('Email')
->canSee(fn () => auth()->user()->isAdmin()),
];
}
Performance Issues
with() or unoptimized where clauses.setQueryCallback() for complex queries that can't be built incrementally:
public function configure(): void
{
$this->setQueryCallback(fn () => $this->buildComplexQuery());
}
Search Not Working
searchable() and that the search fields are included in setSearch().->toSql() to your query.Pagination Conflicts
Builder instance, not a collection or array:
// Wrong:
return User::active()->get();
// Right:
return User::active()->query();
Livewire State Persistence
setPersistState(true) in configure() to save state in session:
public function configure(): void
{
$this->setPersistState(true);
}
Log Queries: Add this to your configure() to log generated queries:
public function configure(): void
{
$this->setQueryLogger(true);
}
Check logs in storage/logs/laravel-livewire-tables.log.
Inspect State: Dump the current table state for debugging:
dd($this->getState());
Disable JavaScript: Test your table with JS disabled to ensure server-side rendering works:
public function configure(): void
{
$this->setDisableClientSide(true);
}
Custom Views Override default views by publishing assets:
php artisan vendor:publish --tag=livewire-tables-views
Then modify resources/views/vendor/livewire-tables/.
Custom Styling Use Tailwind/Blade classes or override the default CSS:
<livewire:admin.users-table :class="'bg-gray-100'" />
Event Handling Listen for table events (e.g., row selection, bulk actions):
protected $listeners = ['rowSelected' => 'handleRowSelection'];
public function handleRowSelection($rowId)
{
$this->selectedRows[] = $rowId;
}
Bulk Actions
Implement bulk actions with setBulkActions():
public function configure(): void
{
$this->setBulkActions([
'delete' => Action::make('Delete Selected')
->action(fn () => $this->deleteSelected()),
]);
}
public function deleteSelected(): void
{
$this->query()->whereIn('id', $this->getSelectedRows())->delete();
$this->dispatch('bulk-action-completed');
}
Reusable Components: Create a base table component for shared functionality:
class BaseTable extends LivewireTable
{
public function configure(): void
{
$this->setPerPage(20);
$this->setSearchDebounce(300);
$this->setEmptyState('No records found.');
}
}
Extend it in child components:
class UsersTable extends BaseTable { ... }
Dynamic Columns: Load columns dynamically based on user roles:
public function columns(): array
{
$columns = ['name', 'email'];
if (auth()->user()->isAdmin()) {
$columns[] = 'deleted_at';
}
return array_map(fn ($col) => Column::make($col), $columns);
}
Export Functionality: Add CSV/Excel export with a single action:
public function actions(): array
{
return [
'export' => Action::make('Export')
->icon('download')
->action(fn () => $this->export()),
];
}
public function export(): void
{
$
How can I help you explore Laravel packages today?