mediconesystems/livewire-datatables
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require mediconesystems/livewire-datatables
Publish the config and migrations:
php artisan vendor:publish --provider="MedicOneSystems\LivewireDatatable\LivewireDatatableServiceProvider"
php artisan migrate
First Usage: Create a Livewire component:
php artisan make:livewire Admin/UsersDatatable
Update the generated component (app/Http/Livewire/Admin/UsersDatatable.php):
use MedicOneSystems\LivewireDatatable\LivewireDatatable;
use App\Models\User;
class UsersDatatable extends LivewireDatatable
{
public function configure(): void
{
$this->setPrimaryKey('id');
$this->setModel(User::class);
$this->setSearch(['name', 'email']);
$this->setPerPage(10);
}
public function columns(): array
{
return [
'name' => 'Name',
'email' => 'Email',
'created_at' => 'Created At',
];
}
}
Render the Datatable:
@livewire('admin.users-datatable')
Replace a basic Laravel paginated list with a feature-rich datatable in minutes. For example, transform a simple User::paginate(10) into a sortable, filterable, and searchable grid with minimal code.
Model Binding:
Bind to Eloquent models or query builders. For complex queries, use the setQuery() method:
public function configure(): void
{
$this->setQuery(User::where('active', true));
}
Column Configuration: Define columns with custom formatting:
public function columns(): array
{
return [
'name' => 'Name',
'email' => (new Column('email'))
->sortable()
->searchable()
->format(fn($value) => '<a href="mailto:'.$value.'">'.$value.'</a>'),
'role' => (new Column('role'))
->format(fn($value) => ucfirst($value)),
];
}
Filtering:
Add filters via addColumn() or setFilters():
public function configure(): void
{
$this->setFilters([
'role' => 'select',
'active' => 'boolean',
'created_at' => 'date-range',
]);
}
Mass Actions: Add bulk actions (e.g., delete, export):
public function configure(): void
{
$this->setActions([
'delete' => 'Delete',
'export' => 'Export',
]);
}
public function deleteSelected()
{
$this->selected()->delete();
}
Column Groups: Group related columns for better UX:
public function columns(): array
{
return [
'contact' => [
'name' => 'Name',
'email' => 'Email',
'phone' => 'Phone',
],
'status' => [
'active' => 'Active',
'role' => 'Role',
],
];
}
Tailwind CSS: The package includes Tailwind classes for styling. Customize via the setTableClasses() method:
public function configure(): void
{
$this->setTableClasses('min-w-full divide-y divide-gray-200');
}
Alpine.js: Leverage Alpine for dynamic interactions (e.g., tooltips, modals):
<td x-data="{ tooltip: false }" @mouseenter="tooltip = true" @mouseleave="tooltip = false">
<span x-text="value"></span>
<span x-show="tooltip" x-transition class="absolute z-10 bg-gray-800 text-white text-xs rounded">
Tooltip content
</span>
</td>
Livewire Hooks: Use Livewire hooks for pre/post actions:
public function boot()
{
$this->dispatchBrowserEvent('datatable-ready');
}
API Integration: Fetch data from external APIs by overriding getData():
public function getData(): array
{
$response = Http::get('https://api.example.com/users');
return $response->json();
}
Performance:
Avoid eager-loading unnecessary relationships in configure(). Use with() sparingly:
// Bad: Loads all relationships for all rows
$this->setModel(User::with('posts', 'comments')->get());
// Good: Load only what's needed
$this->setModel(User::class);
$this->addColumn('posts_count', 'Posts', fn($row) => $row->posts->count());
For large datasets, use setPerPage(50) or implement cursor-based pagination.
Column Formatting:
// Safe
'name' => fn($value) => htmlspecialchars($value),
// Unsafe (if $value is user-provided)
'name' => fn($value) => $value,
Filter Conflicts:
setSearch():
$this->setSearch(['name', 'email']); // Only search these columns
Livewire State:
public function resetState()
{
$this->reset();
}
Unmaintained Package:
MedicOneSystems\LivewireDatatable\Traits\QueryBuilder for complex joins.app/Providers/AppServiceProvider.php:
LivewireDatatable::addFilter('custom', CustomFilter::class);
Log Queries:
Enable query logging in AppServiceProvider:
public function boot()
{
if (config('app.debug')) {
DB::enableQueryLog();
}
}
Log queries in your datatable component:
public function getData(): array
{
$this->logQueries();
return $this->query()->paginate($this->perPage)->toArray();
}
Inspect State: Dump Livewire state to debug filtering/sorting:
public function mount()
{
$this->dispatchBrowserEvent('alert', ['message' => json_encode($this->state)]);
}
Tailwind Conflicts: If styles break, override Tailwind classes in your component’s Blade file:
@push('styles')
<style>
.livewire-datatable th { background-color: #f8fafc !important; }
</style>
@endpush
Custom Columns:
Create reusable column types in app/Models/Columns/CustomColumn.php:
namespace App\Models\Columns;
use MedicOneSystems\LivewireDatatable\Column;
class CustomColumn extends Column
{
public function __construct($column, $label = null)
{
parent::__construct($column, $label);
$this->addClass('text-blue-500');
}
}
Use it in columns():
'custom' => new CustomColumn('email', 'Custom Email'),
Custom Actions: Extend mass actions with custom logic:
public function archiveSelected()
{
$this->selected()->update(['archived_at' => now()]);
$this->dispatchBrowserEvent('alert', ['message' => 'Items archived!']);
}
Localization:
Override default labels (e.g., "No results found") in config/livewire-datatable.php:
'labels' => [
'no_results' => 'No matching records found.',
],
Server-Side Processing:
For large datasets, implement server-side processing by overriding getData():
public function getData(): array
{
return $this->query()
->forPage($this->page, $this->perPage)
->get()
->map(fn($row) => $this->formatRow($row))
How can I help you explore Laravel packages today?