modus-digital / livewire-datatables
Reusable Laravel Livewire datatable for the TALL stack—Tailwind-styled, concern-driven (columns, filters, sorting, pagination, row actions), fully testable with Pest & Larastan-ready.
Fund package maintenance!
modus-digital
Requires
- php: ^8.3
- illuminate/contracts: ^11.0||^12.0
- livewire/livewire: ^3.0
Requires (Dev)
- larastan/larastan: ^2.9||^3.0
- laravel/pint: ^1.22
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/extension-installer: ^1.3||^2.0
- phpstan/phpstan-deprecation-rules: ^1.1||^2.0
- phpstan/phpstan-phpunit: ^1.3||^2.0
- spatie/laravel-package-tools: ^1.16
README
A modern, feature-rich Livewire Datatable component for the TALL stack (Tailwind CSS, Alpine.js, Laravel, Livewire). Built with modularity, performance, and developer experience in mind.
✨ Features
- 🎨 Beautiful Tailwind CSS styling with dark mode support
- 🔍 Global search with debounced input and relationship support
- 🗂️ Advanced filtering with multiple filter types (Text, Select, Date)
- 📊 Column sorting with visual indicators and custom sort fields
- 📄 Pagination with customizable page sizes and navigation
- ✅ Row selection with bulk actions and "select all" functionality
- 🎯 Row actions with customizable buttons and callbacks
- 🖼️ Multiple column types (Text, Icon, Image) with specialized features
- 🏷️ Badge support with dynamic colors and callbacks
- 🔗 Clickable rows with custom handlers
- 🔭 Custom cell views for complex content rendering
- 🚀 Performance optimized with efficient querying
📋 Requirements
Requirement | Version |
---|---|
PHP | ^8.3 |
Laravel | ^11.0 or ^12.0 |
Livewire | ^3.0 |
Tailwind CSS | ^4.0 |
Alpine.js | ^3.0 |
📦 Installation
Install the package via Composer:
composer require modus-digital/livewire-datatables
The package will automatically register its service provider.
Publishing Views (Optional)
To customize the table appearance, publish the views:
php artisan vendor:publish --tag="livewire-datatables-views"
This publishes all Blade templates to resources/views/vendor/livewire-datatables/
.
Publishing Config (Optional)
To customize the package, you can publish the config file:
php artisan vendor:publish --tag="livewire-datatables-config"
This publishes a config file to config/livewire-datatables.php
Source frontend styles
To automatically source the frontend using tailwind, include the following files to the app.css
@source '../../vendor/modus-digital/livewire-datatables/resources/**/*.blade.php'; @source '../../vendor/modus-digital/livewire-datatables/src/**/*.php';
🚀 Quick Start
1. Generate a Table Component
Use the built-in Artisan command to scaffold a new table:
php artisan make:table UsersTable --model=App\\Models\\User
Or create one manually:
<?php declare(strict_types=1); namespace App\Livewire\Tables; use App\Models\User; use ModusDigital\LivewireDatatables\Livewire\Table; use ModusDigital\LivewireDatatables\Columns\Column; use ModusDigital\LivewireDatatables\Columns\TextColumn; use ModusDigital\LivewireDatatables\Filters\SelectFilter; class UsersTable extends Table { protected string $model = User::class; protected function columns(): array { return [ TextColumn::make('Name') ->field('name') ->sortable() ->searchable(), TextColumn::make('Email') ->field('email') ->sortable() ->searchable(), TextColumn::make('Status') ->field('status') ->badge() ->sortable(), TextColumn::make('Created') ->field('created_at') ->sortable() ->format(fn($value) => $value->diffForHumans()), ]; } protected function filters(): array { return [ SelectFilter::make('Status') ->options([ 'active' => 'Active', 'inactive' => 'Inactive', 'banned' => 'Banned', ]) ->multiple(), // <-- This is optional ]; } }
2. Use in Your Blade Template
<livewire:users-table />
📚 Documentation
Column Types
Base Column
De basis met alle kernopties:
Column::make('Name') ->field('name') // Database field (ook: 'relation.field') ->sortable() // Kolom sorteerbaar maken ->searchable() // Opnemen in globale zoek ->hidden() // Verbergen ->width('150px') // CSS breedte (bv. '150px', '20%') ->align('center') // 'left' | 'center' | 'right' ->view('custom.cell') // Custom Blade view voor de cel ->sortField('users.name') // Aparte sorteer-field (optioneel) ->format(fn ($value, $record) => strtoupper((string) $value));
TextColumn
Extra’s voor tekstweergave:
TextColumn::make('Description') ->field('description') ->limit(50) // Tekst afkappen ->badge('blue') // Badge tonen (kleur vast of via Closure) ->fullWidth(); // Badge over volle breedte
IconColumn
IconColumn::make('Status') ->field('is_active') ->icon(fn ($record) => $record->is_active ? 'fa-check' : 'fa-times') ->count(fn ($record) => $record->notifications_count);
ImageColumn
ImageColumn::make('Avatar') ->field('avatar_url') ->src(fn ($record) => $record->getAvatarUrl()) ->circle(); // Rond i.p.v. vierkant
Relatievelden gebruik je met dot-notatie in field
:
Column::make('Department') ->field('department.name') // Relationele kolom ->sortable();
Voor aangepaste SQL sortering via een andere kolom:
Column::make('Department') ->field('department.name') ->sortField('departments.name');
Filters
TextFilter
use ModusDigital\LivewireDatatables\Filters\TextFilter; TextFilter::make('Name') ->field('name') ->placeholder('Search names...') ->contains(); // ook: ->exact(), ->startsWith(), ->endsWith()
SelectFilter
use ModusDigital\LivewireDatatables\Filters\SelectFilter; SelectFilter::make('Status') ->field('status') ->options([ 'active' => 'Active Users', 'inactive' => 'Inactive Users', 'banned' => 'Banned Users', ]) ->placeholder('All Statuses') ->multiple();
DateFilter
use ModusDigital\LivewireDatatables\Filters\DateFilter; DateFilter::make('Created') ->field('created_at') ->range() // datumbereik (from/to) ->format('Y-m-d');
Filters werken ook op relatievelden (field('relation.attribute')
). Indien een veld een Eloquent attribute/accessor is, valt filtering terug op PHP (na ophalen) voor correcte resultaten.
Row selection
Rijselectie inschakelen en gebruiken:
class UsersTable extends Table { // Of via config('livewire-datatables.checkboxes') public bool $showSelection = true; }
Geselecteerde IDs vind je in $this->selected
(array). Combineer dit met een globale actie om bulk-operaties te doen (zie hieronder).
Row Actions
Voeg rijacties met callbacks toe:
use ModusDigital\LivewireDatatables\Actions\RowAction; protected function rowActions(): array { return [ RowAction::make('edit', 'Edit') ->class('text-blue-600') ->callback(fn ($row, $table) => redirect()->route('users.edit', $row)), RowAction::make('delete', 'Delete') ->confirm('Delete this user?') ->callback(function ($row) { $row->delete(); session()->flash('message', 'User deleted successfully.'); }), ]; }
Je kunt zichtbaarheid conditioneel maken met ->visible(fn ($row) => ...)
en een ->icon()
string meegeven.
Global Actions
Header-acties met callback (handig voor bulk op $this->selected
):
use ModusDigital\LivewireDatatables\Actions\Action; protected function actions(): array { return [ Action::make('create', 'Add User') ->class('bg-blue-600 hover:bg-blue-700 text-white') ->callback(fn ($table) => redirect()->route('users.create')), Action::make('delete-selected', 'Delete Selected') ->class('bg-red-600 hover:bg-red-700 text-white') ->confirm('Delete all selected users?') ->callback(function ($table) { $ids = $table->selected; if (! empty($ids)) { \App\Models\User::whereIn('id', $ids)->delete(); $table->deselectAll(); } }), ]; }
Clickable Rows
Maak hele rijen klikbaar door showRecord
te overriden:
public function showRecord(string|int $id): void { // Bijvoorbeeld: redirect()->route('users.show', $id); }
Pagination Configuration
class UsersTable extends Table { public int $perPage = 25; // Standaard page size protected array $perPageOptions = [10, 25, 50, 100]; }
Search Configuration
class UsersTable extends Table { protected bool $searchable = true; protected string $searchPlaceholder = 'Search users...'; }
Empty State Customization
Publiceer de views en pas resources/views/vendor/livewire-datatables/partials/empty-state.blade.php
aan:
<h3 class="mt-2 text-sm font-medium">No records</h3> <p class="mt-1 text-sm">Try adjusting your filters.</p>
Custom Query Building
protected function query(): Builder { return $this->getModel() ->query() ->with(['profile', 'roles']); }
Relationship Handling
Gebruik dot‑notatie in field('relation.column')
. Voor sorting over relaties gebruik je ->sortField('related_table.column')
. Wanneer je sorteert op een attribute/accessor, wordt automatisch in PHP gesorteerd na ophalen.
🎨 Styling & Customization
Dark Mode Support
The package includes full dark mode support using Tailwind's dark:
variants. Ensure your project has dark mode configured:
@source '../../vendor/modus-digital/livewire-datatables/resources/views/**/*.blade.php';
Custom Views
Create custom cell views for complex content:
Column::make('Actions') ->view('components.user-actions') ->width('w-32'),
<!-- resources/views/components/user-actions.blade.php --> <div class="flex space-x-2"> <button wire:click="editUser({{ $record->id }})" class="text-blue-600"> Edit </button> <button wire:click="deleteUser({{ $record->id }})" class="text-red-600"> Delete </button> </div>
Badge Colors
Available badge colors for TextColumn:
gray
(default)red
yellow
green
blue
indigo
purple
pink
TextColumn::make('Status') ->badge(fn($record) => match($record->status) { 'active' => 'green', 'pending' => 'yellow', 'banned' => 'red', default => 'gray' });
🏗️ Architecture
The package follows a modular trait-based architecture:
Core Traits
HasColumns
- Column management and rendering (120 lines)HasFilters
- Filter functionality and application (149 lines)HasPagination
- Pagination configuration (67 lines)HasSorting
- Sorting logic and state management (132 lines)HasRowSelection
- Row selection and bulk actions (142 lines)HasRowActions
- Individual row action handling (92 lines)HasActions
- Global header actions (59 lines)
Each trait is focused, testable, and can be understood independently.
🧪 Testing
The package includes comprehensive tests using Pest 3:
# Run all tests composer test # Run static analysis composer analyse # Fix code style composer format
🔧 Development
Code Quality Tools
The package uses several tools to maintain high code quality:
- Pest 3 - Modern PHP testing framework
- Larastan - Static analysis for Laravel
- Laravel Pint - Code style fixer
- PHPStan - Static analysis with strict rules
Contributing Workflow
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Ensure all tests pass:
composer test
- Fix code style:
composer format
- Run static analysis:
composer analyse
- Submit a pull request
📝 Changelog
See CHANGELOG.md for recent changes and version history.
👥 Credits
- Alex van Steenhoven - Creator & Maintainer
- Modus Digital - Organization
- All Contributors
📄 License
The MIT License (MIT). Please see License File for more information.