timolake/livewire-tables
timolake/livewire-tables provides reusable Livewire components for building interactive, sortable, searchable data tables in Laravel. Create table views with pagination, filters, and column definitions, keeping server-side rendering with a reactive UI and minimal boilerplate.
Install the Package
composer require timolake/livewire-tables
Ensure your project uses Livewire 3.x and Laravel 10/11.
Create a Basic Table Class
Define a Table class for your model (e.g., app/Tables/UserTable.php):
use Timolake\LivewireTables\Table;
class UserTable extends Table
{
public function configure()
{
$this->setPrimaryKey('id');
$this->setSearch(['name', 'email']);
$this->setColumns(['id', 'name', 'email']);
$this->setPagination(10); // Optional: default 15
}
public function query()
{
return User::query();
}
}
Render the Table in Blade
Use the @livewire directive to embed the table:
@livewire('user-table', ['table' => \App\Tables\UserTable::class])
Create a Livewire Component Generate a Livewire component to host the table:
php artisan make:livewire UserTable
Update the generated UserTable.php:
public $table;
public function mount($table)
{
$this->table = $table;
}
public function render()
{
return view('livewire.user-table');
}
Update the Livewire View
Edit resources/views/livewire/user-table.blade.php:
<div>
{!! $this->table->render() !!}
</div>
Replace a manual paginated table loop with the package’s built-in functionality. For example: Before:
<table>
@foreach ($users->paginate(10) as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
</tr>
@endforeach
</table>
After:
@livewire('user-table', ['table' => \App\Tables\UserTable::class])
The package handles pagination, search, and sorting automatically.
Define Table Structure
Use the configure() method to set up columns, searchable fields, and pagination:
public function configure()
{
$this->setPrimaryKey('id');
$this->setSearch(['name', 'email', 'created_at']); // Search across fields
$this->setColumns([
'id' => 'ID',
'name' => 'Name',
'email' => 'Email',
'created_at' => 'Created At',
]);
$this->setSortable(['name', 'email', 'created_at']); // Explicitly set sortable columns
$this->setPagination(20);
}
Customize Column Rendering
Override column rendering in the configure() method or use Blade views:
$this->setColumns([
'id' => 'ID',
'name' => function ($value, $row) {
return Str::upper($value); // Custom logic
},
'email' => LivewireView::make('tables.email-column', ['user' => $row]),
]);
Create a Blade view (resources/views/tables/email-column.blade.php):
<a href="mailto:{{ $user->email }}">{{ $user->email }}</a>
Add Row Actions
Define actions in configure() and render them in the table:
public function configure()
{
$this->addAction('view', 'View', 'livewire.view-user', ['user' => $this->getKey()]);
$this->addAction('edit', 'Edit', 'livewire.edit-user', ['user' => $this->getKey()]);
}
The package renders action buttons by default. Customize the view:
@foreach ($this->table->getActions() as $action)
<td>
{!! $action->render() !!}
</td>
@endforeach
Filtering and Advanced Queries
Use the query() method to build complex queries:
public function query()
{
return User::query()
->when($this->getSearch(), function ($query) {
return $query->where(function ($q) {
$q->where('name', 'like', "%{$this->getSearch()}%")
->orWhere('email', 'like', "%{$this->getSearch()}%");
});
})
->when($this->getSort(), function ($query) {
$query->orderBy($this->getSort()[0], $this->getSort()[1]);
});
}
Bulk Actions
Add bulk actions via the addBulkAction() method:
public function configure()
{
$this->addBulkAction('delete', 'Delete Selected', 'livewire.delete-users', [
'users' => $this->getSelectedKeys(),
]);
}
Render the bulk action dropdown in Blade:
@if ($this->table->hasBulkActions())
<div>
{!! $this->table->renderBulkActions() !!}
</div>
@endif
Reuse Tables Across Components
Create a base Table class for shared configurations:
abstract class BaseTable extends Table
{
public function configure()
{
$this->setPagination(25);
$this->setSearchDebounce(500); // Debounce search input
}
}
Leverage Traits for Shared Logic
Use provided traits (e.g., PaginationTrait) or create custom ones:
use Timolake\LivewireTables\Traits\PaginationTrait;
class UserTable extends Table
{
use PaginationTrait;
public function configure()
{
$this->setPagination(10);
}
}
Customize Styling
Override the default table view (resources/views/vendor/livewire-tables/table.blade.php):
<table class="min-w-full divide-y divide-gray-200">
<!-- Customize table classes and structure -->
</table>
Use Tailwind CSS or your preferred CSS framework for styling.
Session Persistence The package automatically persists search, sort, and pagination states in the session. Customize the session key:
public function configure()
{
$this->setSessionKey('custom_user_table_state');
}
Lazy Loading for Large Datasets For tables with >10K rows, implement cursor pagination:
public function query()
{
return User::query()
->cursor()
->when($this->getSearch(), function ($query) {
// Add search logic
});
}
Integration with Livewire Actions Trigger Livewire actions from table events (e.g., row clicks):
public function render()
{
return view('livewire.user-table', [
'table' => $this->table,
'onRowClick' => function ($row) {
$this->emit('rowClicked', $row->id);
},
]);
}
Listen for events in the parent component:
protected $listeners = ['rowClicked'];
public function rowClicked($id)
{
// Handle row click logic
}
Session State Conflicts
setSessionKey() for each table:
$this->setSessionKey('user_table_state');
Performance with Large Datasets
cursor() for large datasets:
return User::query()->cursor()->paginate(100);
Search Not Working as Expected
LIKE with wildcards:
$query->where('name', 'like', "%{$search}%");
$query->where('name', 'ILIKE', "%{$search}%");
Sorting Not Persisting
How can I help you explore Laravel packages today?