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!
ModusDigital
Requires
- php: ^8.3
- illuminate/contracts: ^10.0||^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||^8.22.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
This package is auto-updated.
Last update: 2025-06-18 08:35:32 UTC
README
A reusable, highly-customizable Livewire Datatable component for the TALL stack (Tailwind CSS, Alpine.js, Laravel, Livewire). Built with modularity, testability, and developer experience in mind.
Features
- 🎨 Beautiful Tailwind CSS styling with dark mode support
- 🔍 Global search with debounced input (300ms)
- 🗂️ Advanced filtering with multiple filter types
- 📊 Column sorting with visual indicators
- 📄 Pagination with customizable page sizes
- ✅ Row selection with bulk actions
- 🔧 Highly customizable with traits and concerns
- 🧪 Fully tested with Pest 3
- 📱 Responsive design for all screen sizes
- ♿ Accessibility features built-in
Package Overview
This repository is a Laravel package that ships a ready-to-use datatable component built with Livewire. The goal is to provide a clean starting point that you can easily extend.
Key directories
src/
– TheTable
Livewire component, traits, column definitions and filters.resources/views/
– Blade templates that render the table.resources/stubs/
– Stub used by themake:table
command.tests/
– Pest tests and architecture rules.
The Table
class orchestrates querying your model, applying search, filters, sorting and pagination so your table class focuses on describing columns and filters.
Installation
You can install the package via composer:
composer require modus-digital/livewire-datatables
Optionally, you can publish the views using:
php artisan vendor:publish --tag="livewire-datatables-views"
Quick Start
1. Create a Table Component
<?php use App\Models\User; use ModusDigital\LivewireDatatables\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(), Column::make('Role') ->relationship('profile', 'role') ->sortable(), TextColumn::make('Status') ->field('status') ->badge() ->limit(10), ]; } 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>
How It Works
The table component builds a query from your model, applies global search, filters and sorting, then paginates the results. Each column can be marked sortable or searchable and may format its value using a callback. Filters implement a simple apply()
method so you can easily add custom logic. The included Blade views render everything with Tailwind classes.
Advanced Usage
Column Configuration
Column::make('Avatar') ->field('avatar_url') ->view('components.avatar') // Custom view ->attributes(['header_class' => 'w-16']), Column::make('Created') ->field('created_at') ->sortable() ->format(fn($value) => $value->diffForHumans()), Column::make('Actions') ->view('components.user-actions') ->attributes(['cell_class' => 'text-right']),
Dynamic Icons & Badges
use ModusDigital\LivewireDatatables\Columns\IconColumn; use ModusDigital\LivewireDatatables\Columns\TextColumn; protected function columns(): array { return [ IconColumn::make('status') ->icon(fn($record) => $record->active ? 'fa-check' : '<svg></svg>') ->count(fn($record) => $record->notifications_count), TextColumn::make('role') ->badge(fn($record) => $record->role_color), ]; }
Row Selection & Bulk Actions
class UsersTable extends Table { public bool $enableRowSelection = true; protected function bulkActions(): array { return [ [ 'name' => 'Delete Selected', 'key' => 'delete', 'class' => 'bg-red-600 hover:bg-red-700', ], [ 'name' => 'Export Selected', 'key' => 'export', 'class' => 'bg-green-600 hover:bg-green-700', ], ]; } public function bulkActionDelete($rows) { $rows->each->delete(); session()->flash('message', 'Selected users deleted successfully.'); } public function bulkActionExport($rows) { // Export logic here } }
Row Actions
use ModusDigital\LivewireDatatables\Actions\RowAction; protected function rowActions(): array { return [ RowAction::make('edit', 'Edit')->icon('<path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/>'), RowAction::make('delete', 'Delete')->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
use ModusDigital\LivewireDatatables\Actions\Action; protected function globalActions(): array { return [ Action::make('create', 'Add User') ->class('bg-orange-600 hover:bg-orange-700') ->label('+ Add User'), ]; } public function globalAction($action) { if ($action === 'create') { return redirect()->route('users.create'); } }
Custom Empty State
public string $emptyStateTitle = 'No users found'; public string $emptyStateDescription = 'Get started by creating your first user.';
Pagination Configuration
public int $perPage = 25; public array $perPageOptions = [10, 25, 50, 100]; public bool $showPerPageSelector = true;
Customization
Publishing Views
To customize the table appearance, publish the views:
php artisan vendor:publish --tag="livewire-datatables-views"
This will publish all Blade templates to resources/views/vendor/livewire-datatables/
.
Styling
The package uses Tailwind CSS classes exclusively. You can:
- Override CSS classes by modifying the published views
- Add custom attributes to columns using the
attributes()
method - Use custom views for specific columns with the
view()
method
Dark Mode
Dark mode is supported out of the box using Tailwind's dark:
variants. Ensure your project has dark mode configured in tailwind.config.js
:
module.exports = { darkMode: 'class', // or 'media' // ... rest of config }
Testing
The package includes comprehensive tests using Pest 3:
composer test
All traits are individually tested with feature tests covering:
- Column functionality
- Filtering and search
- Sorting mechanisms
- Pagination
- Row selection
- Bulk actions
Architecture
The package follows a modular architecture using traits:
HasColumns
- Column management and renderingHasFilters
- Filter functionalityHasPagination
- Pagination configurationHasSorting
- Sorting logicHasRowSelection
- Row selection and bulk actionsHasRowActions
- Individual row actions
Each trait is under 150 lines of code and fully unit tested.
Next Steps
- Browse the traits in
src/Concerns
to understand how each feature works. - Customize the Blade templates in
resources/views
to match your design. - Use the
make:table
Artisan command to scaffold new tables from the provided stub. - Run
composer analyse
andcomposer test
to ensure quality as you extend the package.
Requirements
- PHP 8.3+
- Laravel 10.0+ | 11.0+ | 12.0+
- Livewire 3.0+
- Tailwind CSS 3.0+
- Alpine.js 3.0+
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.