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
- spatie/laravel-package-tools: ^1.16
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
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
- π§ Highly customizable with trait-based architecture
- πΌοΈ 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
- π± Responsive design for all screen sizes
- βΏ Accessibility features built-in
- π 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/
.
π 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 [ Column::make('Name') ->field('name') ->sortable() ->searchable(), Column::make('Email') ->field('email') ->sortable() ->searchable(), TextColumn::make('Status') ->field('status') ->badge() ->sortable(), Column::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', ]), ]; } }
2. Use in Your Blade Template
<div> <livewire:users-table /> </div>
π Documentation
Column Types
Base Column
The foundation for all column types with essential features:
Column::make('Name') ->field('name') // Database field ->sortable() // Enable sorting ->searchable() // Include in global search ->hidden() // Hide column ->width('w-32') // Set width classes ->align('center') // Alignment: left, center, right ->view('custom.cell') // Custom view ->relationship('profile.role') // Access relationship data ->sortField('custom_sort_field') // Custom sort field ->format(fn($value, $record) => strtoupper($value)); // Format callback
TextColumn
Specialized for text content with additional features:
TextColumn::make('Description') ->field('description') ->limit(50) // Truncate text ->badge() // Render as badge ->badge('blue') // Badge with specific color ->badge(fn($record) => $record->priority_color) // Dynamic badge color ->fullWidth(); // Badge spans full cell width
IconColumn
Display icons with optional counts:
IconColumn::make('Status') ->field('is_active') ->icon(fn($record) => $record->is_active ? 'fa-check' : 'fa-times') ->icon('<svg>...</svg>') // Static SVG icon ->count(fn($record) => $record->notifications_count); // Show count badge
ImageColumn
Display images with fallback support:
ImageColumn::make('Avatar') ->field('avatar_url') ->src(fn($record) => $record->getAvatarUrl()) // Dynamic source ->fallback('/images/default-avatar.png') // Fallback image ->rounded() // Apply rounded styling ->size('w-10 h-10'); // Size classes
Filters
TextFilter
Search within specific fields:
TextFilter::make('Name') ->field('name') ->placeholder('Search names...') ->operator('like'); // Operators: like, =, !=, >, <, >=, <=
SelectFilter
Dropdown selection with predefined options:
SelectFilter::make('Status') ->field('status') ->options([ 'active' => 'Active Users', 'inactive' => 'Inactive Users', 'banned' => 'Banned Users', ]) ->placeholder('All Statuses') ->multiple(); // Allow multiple selections
DateFilter
Date range filtering:
DateFilter::make('Created') ->field('created_at') ->placeholder('Select date range...') ->format('Y-m-d'); // Date format
Row Selection & Bulk Actions
Enable row selection and define bulk actions:
class UsersTable extends Table { public bool $showSelection = true; // Enable row selection protected function bulkActions(): array { return [ [ 'name' => 'Delete Selected', 'key' => 'delete', 'class' => 'bg-red-600 hover:bg-red-700 text-white', ], [ 'name' => 'Export Selected', 'key' => 'export', 'class' => 'bg-green-600 hover:bg-green-700 text-white', ], ]; } public function bulkActionDelete($rows) { $rows->each->delete(); session()->flash('message', 'Selected users deleted successfully.'); } public function bulkActionExport($rows) { // Export logic here return response()->download($this->generateExport($rows)); } }
Row Actions
Add action buttons to each row:
use ModusDigital\LivewireDatatables\Actions\RowAction; protected function rowActions(): array { return [ RowAction::make('edit', 'Edit') ->icon('<svg>...</svg>') ->class('text-blue-600 hover:text-blue-900'), RowAction::make('delete', 'Delete') ->icon('<svg>...</svg>') ->class('text-red-600 hover:text-red-900'), ]; } public function rowActionEdit($row) { return redirect()->route('users.edit', $row); } public function rowActionDelete($row) { $row->delete(); session()->flash('message', 'User deleted successfully.'); }
Global Actions
Add header-level actions:
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') ->label('+ Add User'), ]; } public function globalAction($action) { if ($action === 'create') { return redirect()->route('users.create'); } }
Clickable Rows
Make entire rows clickable:
class UsersTable extends Table { public function showRecord(string|int $id): void { // Redirect to detail page return redirect()->route('users.show', $id); // Or dispatch Livewire event // $this->dispatch('openUserDrawer', id: $id); } }
Pagination Configuration
Customize pagination behavior:
class UsersTable extends Table { public int $perPage = 25; // Default page size public array $perPageOptions = [10, 25, 50, 100]; // Available options public bool $showPerPageSelector = true; // Show page size selector }
Search Configuration
Customize search behavior:
class UsersTable extends Table { protected bool $searchable = true; // Enable global search protected string $searchPlaceholder = 'Search users...'; // Custom placeholder }
Empty State Customization
Customize the empty state message:
class UsersTable extends Table { public string $emptyStateTitle = 'No users found'; public string $emptyStateDescription = 'Get started by creating your first user.'; }
Custom Query Building
Override the base query for complex scenarios:
protected function query(): Builder { return $this->getModel() ->query() ->with(['profile', 'roles']) ->where('tenant_id', auth()->user()->tenant_id); }
Relationship Handling
Access relationship data in columns:
Column::make('Role') ->relationship('profile.role') // Nested relationship ->searchable() // Will search in relationship ->sortable(), // Will sort by relationship field Column::make('Department') ->field('department_id') ->relationship('department.name') ->sortField('departments.name'), // Custom sort field for relationship
π¨ 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.
Directory Structure
src/
βββ Actions/ # Action classes for global and row actions
βββ Columns/ # Column type classes with specialized features
βββ Commands/ # Artisan command for generating tables
βββ Concerns/ # Traits for modular functionality
βββ Filters/ # Filter classes for different data types
βββ Livewire/ # Main Table component
βββ LivewireDatatablesServiceProvider.php
resources/
βββ stubs/ # Template for make:table command
βββ views/
βββ partials/ # Reusable view components
βββ table.blade.php # Main table view
tests/
βββ Feature/ # Integration tests
βββ Unit/ # Unit tests for each component
βββ Fixtures/ # Test data and models
π§ͺ Testing
The package includes comprehensive tests using Pest 3:
# Run all tests composer test # Run tests with coverage composer test:coverage # Run static analysis composer analyse # Fix code style composer format
Test Coverage
- β All traits individually tested
- β Column types and their features
- β Filter functionality and operators
- β Sorting mechanisms and edge cases
- β Pagination behavior
- β Row selection and bulk actions
- β Search functionality including relationships
- β Architecture rules with Pest Arch plugin
π§ 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.