alp-develop/laravel-livewire-tables
Reactive Livewire data tables for Laravel—search, sort, filter, paginate, export, and bulk actions with zero JavaScript. Supports Laravel 10–13, Livewire 3–4, PHP 8.1+, Tailwind or Bootstrap 4/5, plus dark mode and configurable themes.
Full-featured, reactive data tables for Laravel. Search, sort, filter, paginate, export, bulk actions — zero JavaScript.
Laravel 10–13 | Livewire 3–4 | PHP 8.1–8.5 | Tailwind / Bootstrap 5 / Bootstrap 4 | Dark mode
composer require alp-develop/laravel-livewire-tables
php artisan vendor:publish --tag=livewire-tables-config
This creates config/livewire-tables.php. This step is required — the config defines the theme, colors, dark mode, and other essential settings.
Set the theme in config/livewire-tables.php:
'theme' => 'tailwind',
| Theme | Value | Alias |
|---|---|---|
| Tailwind CSS | tailwind |
— |
| Bootstrap 5 | bootstrap-5 |
bootstrap5, bootstrap |
| Bootstrap 4 | bootstrap-4 |
bootstrap4 |
Add to your CSS: [x-cloak] { display: none !important; }
php artisan make:livewiretable UsersTable User
<?php
namespace App\Livewire\Tables;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Livewire\Tables\Columns\TextColumn;
use Livewire\Tables\Columns\BooleanColumn;
use Livewire\Tables\Columns\ActionColumn;
use Livewire\Tables\Filters\SelectFilter;
use Livewire\Tables\Livewire\DataTableComponent;
class UsersTable extends DataTableComponent
{
public function configure(): void
{
$this->setDefaultPerPage(25);
$this->setSearchDebounce(300);
}
public function query(): Builder
{
return User::query();
}
public function columns(): array
{
return [
TextColumn::make('name')->sortable()->searchable(),
TextColumn::make('email')->sortable()->searchable(),
BooleanColumn::make('active')->sortable(),
TextColumn::make('created_at')
->label('Joined')->sortable()
->format(fn ($value) => $value?
->format('M d, Y')),
ActionColumn::make()
->button('Edit', fn ($row) => "edit({$row->id})", 'lt-btn-primary')
->button('Delete', fn ($row) => "delete({$row->id})", 'lt-btn-primary'),
];
}
public function filters(): array
{
return [
SelectFilter::make('active')
->label('Status')
->setOptions(['' => 'All', '1' => 'Active', '0' => 'Inactive'])
->filter(fn (Builder $q, $v) => $q->where('active', (bool) $v)),
];
}
public function bulkActions(): array
{
return [
'deleteSelected' => 'Delete Selected',
'exportCsvAuto' => 'Export CSV',
];
}
public function deleteSelected(): void
{
User::whereIn('id', $this->getSelectedIds())->delete();
}
public function edit(int $id): void
{
$this->redirect(route('users.edit', $id));
}
public function delete(int $id): void
{
User::findOrFail($id)->delete();
}
}
<livewire:tables.users-table />
When rendering multiple instances of the same table component (or different tables that share the same class), assign a unique table-key to each one so their state (filters, search, sorting, pagination) is isolated:
<livewire:tables.users-table table-key="users-active" />
<livewire:tables.users-table table-key="users-archived" />
You can also pass it dynamically:
<livewire:tables.users-table :table-key="'users-' . $section" :table-theme="$theme" />
If you don't set
table-key, all instances of the same component will share state via session.
| Guide | |
|---|---|
| Installation | Setup, config, publishing assets |
| Configuration | Config reference, per-table options, table key |
| Columns | Text, Boolean, Date, Image, Action, Blade |
| Filters | All types, dependent filters, custom logic |
| Bulk Actions | Selection model, custom actions, CSV export |
| Export | Auto CSV, custom exports |
| Events & Hooks | Lifecycle hooks, external refresh |
| Toolbar Slots | 6 hook points for custom content |
| Theming | Themes, dark mode, color palette |
| Dark Mode | .lt-dark class, session detection, $this->darkMode |
| Joins | Joined columns, aliases, search on joins |
| Security | Built-in protections, safe callbacks |
composer test # Run tests
composer analyse # PHPStan level 8
composer format # Pint code style
How can I help you explore Laravel packages today?