hubsoluciones / livewire-flux-tables
Reusable Livewire-first tables for Laravel with Flux UI styling.
Package info
github.com/HUB-Soluciones/livewire-flux-tables
pkg:composer/hubsoluciones/livewire-flux-tables
Requires
- php: ^8.1
- illuminate/contracts: ^10.0|^11.0|^12.0|^13.0
- illuminate/database: ^10.0|^11.0|^12.0|^13.0
- illuminate/pagination: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- illuminate/view: ^10.0|^11.0|^12.0|^13.0
- livewire/livewire: ^3.5.19|^4.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.0|^11.0|^12.0
README
A Laravel package for building reusable, Livewire-first data tables styled for Flux UI and Tailwind CSS.
Requirements
- PHP 8.1+
- Laravel 10+
- Livewire 3.5.19+ or 4.0+
- Tailwind CSS 4.0+
Installation
composer require hubsoluciones/livewire-flux-tables
Optionally publish the config, views, or stubs:
php artisan vendor:publish --tag=livewire-flux-tables-config php artisan vendor:publish --tag=livewire-flux-tables-views php artisan vendor:publish --tag=livewire-flux-tables-stubs
Claude Code Skill
If you use Claude Code, you can publish the skill for this package into your project so Claude understands how to work with livewire-flux-tables correctly:
php artisan vendor:publish --tag=livewire-flux-tables-skill
This copies .claude/skills/laravel-flux-table-package/SKILL.md into your project root. Claude Code picks it up automatically and uses it to guide column definitions, filters, sticky columns, custom cells, Artisan commands, and more.
To update the skill after upgrading the package:
php artisan vendor:publish --tag=livewire-flux-tables-skill --force
Quick Start
Generate a table component using the Artisan command:
php artisan livewire-flux-tables:make UsersTable --model=User
Or scaffold one with a view and filter methods:
php artisan livewire-flux-tables:scaffold UsersTable --model=User --path=Tables/Users --view-path=livewire/tables/users --view --with-filters --with-filter-methods
Then render it in any Blade view:
<livewire:tables.users.users-table />
Creating a Table
Extend FluxTableComponent and implement columns() plus one data source method:
use App\Models\User; use HubSoluciones\LivewireFluxTables\Columns\Column; use HubSoluciones\LivewireFluxTables\Livewire\FluxTableComponent; class UsersTable extends FluxTableComponent { public function builder() { return User::query(); } public function columns(): array { return [ Column::make('ID', 'id')->sortable(), Column::make('Name', 'name')->searchable()->sortable()->sticky()->width('14rem'), Column::make('Email', 'email')->searchable()->sortable(), Column::make('Role', 'role')->sortable(), ]; } }
Data Sources
The component auto-detects the data source method in this order:
| Method | Type |
|---|---|
builder() |
Eloquent Builder |
query() |
Query Builder (DB::table(...)) |
records() |
Collection or array |
All three support search, filtering, sorting, and pagination — SQL operations are used for builders, in-memory PHP for collections.
Default Sort
protected function defaultSort(): ?string { return 'created_at'; } protected function defaultSortDirection(): string { return 'desc'; }
Columns
Column::make('Label', 'field_name') ->sortable() // enable sorting ->searchable() // include in global search ->sticky('left') // fixed column (left or right) ->width('14rem') // set column width ->align('center') // text alignment ->format(fn ($value, $row) => ...) // format the displayed value ->view('my-package::cell-view') // use a custom Blade view for the cell ->html() // render value as raw HTML ->default('—') // fallback when value is null/empty ->mobileHidden() // hide on small screens ->mobileLabel('Alt label') // override label on mobile ->stackOnMobile() // stack cell vertically on mobile
Custom Sort or Search Logic
Column::make('Full Name', 'name') ->searchUsing(fn ($query, $term) => $query->orWhere('first_name', 'like', "%$term%") ->orWhere('last_name', 'like', "%$term%")) ->sortableUsing(fn ($query, $direction) => $query->orderBy('last_name', $direction));
Relationship Fields
Dot-notation fields automatically use whereHas for search:
Column::make('Team', 'team.name')->searchable()->sortable(),
Custom Cell View
The view receives $row, $value, $column, and $component:
{{-- resources/views/cells/status.blade.php --}} <span class="badge">{{ $value }}</span>
Column::make('Status', 'status')->view('cells.status'),
Filters
Available Filter Types
| Class | Input | Default behavior |
|---|---|---|
TextFilter |
Text input | LIKE %value% |
SelectFilter |
Dropdown | Exact match (where field = value) |
DateFilter |
Date picker | whereDate field = value |
DateRangeFilter |
Two date pickers (from/to) | whereDate >= and whereDate <= |
use HubSoluciones\LivewireFluxTables\Filters\SelectFilter; use HubSoluciones\LivewireFluxTables\Filters\DateRangeFilter; public function filters(): array { return [ SelectFilter::make('Role', 'role') ->options(['admin' => 'Admin', 'user' => 'User']) ->placeholder('All roles'), DateRangeFilter::make('Created', 'created_at'), ]; }
Custom Filter Logic
Three ways to customize how a filter applies, in order of priority:
1. Inline callback:
SelectFilter::make('Active', 'is_active') ->applyUsing(fn ($query, $value) => $query->where('active', (bool) $value));
2. Component method (convention-based):
// Filter key: 'created_at' → method: applyCreatedAtFilter public function applyCreatedAtFilter($query, $value): void { $query->whereYear('created_at', $value); }
3. Default behavior defined in the filter class itself.
Configuration
After publishing, edit config/livewire-flux-tables.php:
'default_per_page' => 15, 'per_page_options' => [10, 15, 25, 50, 100], 'persist_query_string' => true, // sync state to URL query params 'search_placeholder' => 'Search...', 'default_sticky_width' => '12rem', 'table_wrapper_class' => '...', // Tailwind classes for the outer wrapper 'table_scroll_class' => 'overflow-x-auto', 'empty_state_heading' => 'No results', 'empty_state_message' => '...', 'pagination' => 'length_aware', // or 'simple' 'stubs_path' => 'stubs/livewire-flux-tables',
Query String Persistence
When persist_query_string is enabled (default), all table state (search, sort, filters, page, perPage) is automatically synced to the URL. Each filter gets its own query parameter using the filter key as the alias.
To disable for a specific table, override in your component:
protected function usesQueryStringPersistence(): bool { return false; }
Translations
The package ships with English and Spanish translations. By default it uses your Laravel app's locale (app()->getLocale()).
Publish the language files to customize or add new locales:
php artisan vendor:publish --tag=livewire-flux-tables-lang
Files are published to lang/vendor/livewire-flux-tables/.
Force a specific locale regardless of the app locale:
// config/livewire-flux-tables.php 'locale' => 'es', // 'en', 'es', or null (default — uses app locale)
Add a new locale by creating lang/vendor/livewire-flux-tables/{locale}.json using the English keys:
{
"Search records...": "Rechercher...",
"Search": "Rechercher",
"Records per page": "Enregistrements par page",
"All": "Tous",
"No results": "Aucun résultat",
"No records match the current criteria.": "Aucun enregistrement ne correspond aux critères actuels."
}
Override individual strings without touching translation files — set the value directly in the config:
'search_placeholder' => 'Type to filter...', 'empty_state_heading' => 'Nothing here', 'empty_state_message' => 'Try adjusting your filters.',
Column labels and filter labels (e.g.
Column::make('Name', 'name')) are developer-supplied. Pass__('Name')directly if you want them to be translatable.
Testing
composer test # Single file phpunit tests/Feature/FluxTableComponentTest.php # Single method phpunit tests/Feature/FluxTableComponentTest.php --filter test_global_search_filters_only_searchable_columns
License
MIT