Installation:
composer require v-e-y/datatables-livewire
php artisan vendor:publish --provider="VEY\DataTablesLivewire\DataTablesLivewireServiceProvider"
This publishes the default config and views to resources/views/vendor/datatables-livewire and config/datatables-livewire.php.
Basic Livewire Component: Create a new Livewire component:
php artisan make:livewire UserDataTable
Update the component to extend VEY\DataTablesLivewire\DataTablesLivewire:
<?php
namespace App\Http\Livewire;
use VEY\DataTablesLivewire\DataTablesLivewire;
use App\Models\User;
class UserDataTable extends DataTablesLivewire
{
public function getModel()
{
return User::query();
}
public function getColumns()
{
return [
'id' => 'ID',
'name' => 'Name',
'email' => 'Email',
];
}
}
Blade View: Use the provided view in your blade template:
@livewire('user-data-table')
Display a paginated, sortable, and searchable table of users with minimal effort. The package handles:
Define Data Source:
Override getModel() to return an Eloquent query builder or model instance. Supports eager loading:
public function getModel()
{
return User::with('roles', 'posts')->query();
}
Column Configuration:
Define columns in getColumns(). Use callbacks for custom formatting:
public function getColumns()
{
return [
'id' => 'ID',
'name' => 'Name',
'email' => function ($query, $column) {
return $query->selectRaw("CONCAT(name, ' (', email, ')') as {$column}");
},
'created_at' => (new \VEY\DataTablesLivewire\Columns\DateTimeColumn('created_at'))
->format('m/d/Y H:i'),
];
}
Filtering:
Add filters via getFilters():
public function getFilters()
{
return [
'role' => ['type' => 'select', 'options' => ['admin', 'user']],
'active' => ['type' => 'boolean'],
'search' => ['type' => 'text', 'placeholder' => 'Search users...'],
];
}
Actions:
Add mass actions (bulk operations) in getActions():
public function getActions()
{
return [
'delete' => [
'label' => 'Delete Selected',
'icon' => 'trash',
'method' => 'deleteSelected',
'confirm' => 'Are you sure?',
],
];
}
Complex Queries:
Use getQuery() to modify the base query dynamically:
public function getQuery()
{
$query = $this->getModel();
if ($this->role) {
$query->whereHas('roles', fn($q) => $q->where('name', $this->role));
}
return $query;
}
<x-datatables-livewire.wire:{{ $this }} :columns="{{ $columns }}">
<x-slot name="filters">
@include('livewire.user-data-table.filters')
</x-slot>
</x-datatables-livewire.wire>
getData() method to fetch raw data for custom client-side processing:
public function getData()
{
return $this->getModel()->get()->toArray();
}
Column Naming Conflicts:
Avoid column names that conflict with Laravel's reserved keywords (e.g., created_at vs. createdAt). Use aliases:
'created_at' => ['column' => 'created_at', 'label' => 'Created At'],
Eager Loading Pitfalls:
Ensure eager-loaded relationships are properly configured in getModel() to avoid N+1 queries. Use with() or loadMissing():
public function getModel()
{
return User::with(['posts' => function($query) {
$query->where('published', true);
}])->query();
}
Filter Persistence:
Filters are not automatically persisted across page reloads. Use Livewire's persist() or session storage:
protected $persistentFilters = ['role', 'active'];
Alpine.js Conflicts: If using Alpine.js, ensure no ID conflicts with Livewire's wire:model bindings. Prefix Alpine attributes:
<input x-model="alpineFilter" wire:ignore>
Query Logging: Enable Laravel's query logging to debug complex queries:
\DB::enableQueryLog();
$query = $this->getQuery()->toSql();
\Log::info($query, \DB::getQueryLog());
Livewire Hooks: Use Livewire's lifecycle hooks to debug component state:
public function mount()
{
\Log::info('Component mounted', ['filters' => $this->filters]);
}
Column Groups: Group related columns for better UI organization:
public function getColumns()
{
return [
'group:contact' => [
'name' => 'Name',
'email' => 'Email',
'phone' => 'Phone',
],
];
}
Custom Views: Override the default views by publishing and modifying them:
php artisan vendor:publish --tag=datatables-livewire-views
Then extend the published views in resources/views/vendor/datatables-livewire.
Performance: For large datasets, use database-level filtering and pagination:
public function getQuery()
{
return $this->getModel()->where('active', true);
}
Extending Functionality:
Create custom column types by extending VEY\DataTablesLivewire\Columns\Column:
namespace App\Livewire\Columns;
use VEY\DataTablesLivewire\Columns\Column;
class StatusColumn extends Column
{
public function render($value)
{
return match($value) {
'active' => '<span class="text-green-500">Active</span>',
default => '<span class="text-red-500">Inactive</span>',
};
}
}
Then use it in getColumns():
'status' => new StatusColumn('status'),
Localization: Customize labels and messages via the config file:
'labels' => [
'noRecordsFound' => 'No records found.',
'processing' => 'Processing...',
],
How can I help you explore Laravel packages today?