Reusable Livewire table component kit with:
composer require unlab/livewire-table-kit
Add the package path to your Tailwind configuration to ensure styles are compiled.
For Tailwind v4 (app.css):
@source "../../vendor/unlab/livewire-table-kit/resources/views/**/*.blade.php";
For Tailwind v3 (tailwind.config.js):
content: [
'./vendor/unlab/livewire-table-kit/resources/views/**/*.blade.php',
],
Then run: npm run build
php artisan vendor:publish --tag=livewire-table-kit-views
php artisan vendor:publish --tag=livewire-table-kit-config
php artisan vendor:publish --tag=livewire-table-kit-mcp
php artisan vendor:publish --tag=livewire-table-kit-stubs
php artisan vendor:publish --tag=livewire-table-kit-lang
If you want to use the package MCP server or project-local AI skills:
# Install local MCP server config
php artisan livewire-table-kit:install-mcp
# Install skill files for Codex and project-local workflows
php artisan livewire-table-kit:install-skill
Use the package generator command to scaffold a table component from an Eloquent model:
php artisan make:livewire-table App\\Models\\User UsersTable
The generator uses schema heuristics to automatically determine:
<?php
declare(strict_types=1);
namespace App\Livewire\Tables;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Unlab\LivewireTableKit\Livewire\Components\Tables\BaseTable;
use Unlab\LivewireTableKit\Livewire\Components\Tables\Columns\Column;
use Unlab\LivewireTableKit\Livewire\Components\Tables\Columns\BadgeColumn;
use Unlab\LivewireTableKit\Livewire\Components\Tables\Columns\ActionColumn;
use Unlab\LivewireTableKit\Livewire\Components\Tables\Columns\Actions\TableAction;
use Unlab\LivewireTableKit\Livewire\Components\Tables\Filters\Filter;
class UsersTable extends BaseTable
{
public function query(): Builder
{
return User::query();
}
public function columns(): array
{
return [
Column::make('ID')->field('id')->sortable(),
Column::make('Name')
->field('name')
->searchable()
->sortable(),
Column::make('Email')
->field('email')
->searchable()
->sortable(),
BadgeColumn::make('Status')
->field('status')
->colorMap([
'active' => 'success',
'pending' => 'warning',
'inactive' => 'danger',
]),
ActionColumn::make()->actions([
TableAction::wire('Edit', fn ($row) => "edit('{$row->id}')", icon: 'pencil'),
TableAction::link('View', fn ($row) => route('users.show', $row), icon: 'eye'),
]),
];
}
public function filters(): array
{
return [
Filter::select('role', 'Role', [
'admin' => 'Administrator',
'user' => 'User',
]),
Filter::radio('status', 'Status', [
'active' => 'Active',
'inactive' => 'Inactive',
])->placeholder('All statuses')
->display('dropdown'),
Filter::checkbox('department', 'Departments', [
'engineering' => 'Engineering',
'product' => 'Product',
'support' => 'Support',
]),
Filter::date('created_at', 'Created After'),
// Custom query filter
Filter::text('search_bio', 'Search Bio')
->query(fn ($query, $value) => $query->where('bio', 'like', "%{$value}%")),
];
}
public function supportsExport(): bool => true;
public function supportsBulkDelete(): bool => true;
public function actionBulkDelete(array $payload): void
{
User::whereIn('id', $payload)->delete();
}
}
Column::make('Label'): Create a column.->field('db_column'): Maps to a database column.->value(fn ($row) => ...): Define a custom value resolver.->searchable(?string $field = null): Enable search.->searchableRaw(string $expression): Search using a raw SQL expression (e.g., CONCAT(first_name, ' ', last_name)).->sortable(?string $field = null): Enable sorting.->view('custom.cell-view'): Render cells using a custom Blade view.->align('center'): Set text alignment (left, center, right).->headerAlign('center'): Set header alignment.->html(): Treat the value as raw HTML.->exportable(false): Exclude from exports.BadgeColumn uses Flux UI's badge styles.
BadgeColumn::make('Status')
->field('status')
->colorMap([
'active' => 'success', // emerald
'pending' => 'warning', // amber
'inactive' => 'danger', // rose
'default' => 'default', // zinc
]);
Available colors: primary, success, warning, danger, default.
Filter::select(key, label, options)Filter::radio(key, label, options)Filter::checkbox(key, label, options)Filter::text(key, label, placeholder)Filter::date(key, label)Filter::number(key, label, placeholder)For select and radio filters, placeholder() is used as the label for the reset or "all" option.
Checkbox filters are multi-select, render inside a dropdown in the default toolbar UI, and apply a whereIn-style match by default.
Option-based filters can also control their toolbar presentation with ->display('inline') or ->display('dropdown').
At the table level, you can collapse the entire filter set into a single dropdown trigger:
protected function filterToolbarMode(): string
{
return 'dropdown';
}
The component listens for and dispatches several events:
refreshTable: (Inbound) Refreshes the table data and resets pagination.openBulkDeleteConfirm: (Outbound) Triggered when bulk delete is clicked.bulkDeleteConfirmed: (Inbound) Triggered when the user confirms deletion.notify: (Outbound) Standard notification event with type and message.Override these methods in your table class to customize behavior:
| Method | Description | Default |
|---|---|---|
defaultSortField() |
Initial sort column | null |
defaultSortDirection() |
Initial sort direction | 'asc' |
perPageOptions() |
Options for the per-page selector | [10, 25, 50, 100, 'all'] |
emptyState() |
Configure the empty state display | Array with title/message |
exportType() |
Export processing mode: sync or queued (queue alias accepted) |
'sync' |
exportFilename(ext) |
Name of the exported file | Derived from class name |
For large exports, queue the export job instead of generating the file during the Livewire request:
protected function exportType(): string
{
return 'queued';
}
Queued exports store the generated file on the configured disk and dispatch an exportQueued event with format, disk, and path. The table UI shows a Flux-styled loading toast while the export is processing, then a permanent ready toast with a Download action after the file exists. If the job fails, the table shows a permanent failed toast with the cached error message. Ready and failed toasts stay visible until the user closes them.
protected function exportPdfTitle(): string => 'User Report';
protected function exportPdfOrientation(): string => 'landscape'; // portrait or landscape
protected function exportPdfPaperSize(): string => 'a4';
protected function exportPdfMargins(): array => ['top' => '10mm', ...];
protected function exportPdfFontSize(): string => '9px';
The MIT License (MIT). Please see LICENSE for more information.
How can I help you explore Laravel packages today?