hubsoluciones/livewire-flux-tables

Reusable Livewire-first tables for Laravel with Flux UI styling.

Maintainers

Package info

github.com/HUB-Soluciones/livewire-flux-tables

pkg:composer/hubsoluciones/livewire-flux-tables

Statistics

Installs: 8

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.2.0 2026-04-25 07:36 UTC

This package is auto-updated.

Last update: 2026-04-25 07:37:15 UTC


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.

Selection & Bulk Actions

Add SelectionColumn::make() as the first column to enable row selection. The package automatically renders a sky-toned selection banner above the table with a row counter, "Select all / Clear selection" controls, and inline bulk action buttons — no extra wrapper Blade needed.

{{-- This is all you need in your view --}}
<livewire:tables.socios.socios-tabla />
use HubSoluciones\LivewireFluxTables\Columns\SelectionColumn;

public function columns(): array
{
    return [
        SelectionColumn::make()
            ->resource('socio', 'socios')       // singular / plural for banner copy
            ->bulkActions([
                // string form — backwards-compatible
                'markInactive' => 'Mark inactive',

                // array form — adds icon and variant
                'export' => [
                    'label'   => 'Export selected',
                    'icon'    => 'arrow-down-tray', // generic SVG; publish view for real icons
                    'variant' => 'primary',          // 'primary' | 'danger' | 'default'
                ],
            ]),
        Column::make('Name', 'name')->sortable(),
        // ...
    ];
}

public function export(): void
{
    // $selectAllRecords=true means all filtered records are selected (not just the page).
    // Use allFilteredKeys() to get all matching IDs without pagination.
    $ids = $this->selectAllRecords ? $this->allFilteredKeys() : $this->selectedKeys;
    // act on $ids...
    $this->clearSelection();
}

The header checkbox opens a dropdown with "Select page (N)", "Select all M records", and "Clear selection". Rows can be conditionally disabled with ->selectableWhen(fn ($row) => ...).

Defaults (overridable): sticky-left, width 3rem, centered, not hideable. Use ->notSticky() to remove the sticky behavior.

Customize banner colors (no need to publish the view):

// config/livewire-flux-tables.php
'selection_banner_class'      => '...', // outer wrapper — default: sky-50/sky-950 with border
'selection_banner_text_class' => '...', // counter text — default: text-sky-700/sky-300
'selection_banner_link_class' => '...', // links — default: sky underlined

Generate with: php artisan livewire-flux-tables:make MyTable --with-selection

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',
// Selection banner (sky by default — override without publishing the view):
'selection_banner_class'      => '...',   // outer wrapper classes
'selection_banner_text_class' => '...',   // counter text classes
'selection_banner_link_class' => '...',   // "Select all" / "Clear selection" link classes

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