Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Wired Tables Laravel Package

defstudio/wired-tables

defstudio/wired-tables is a Laravel Livewire package for building interactive data tables. It helps you render sortable, searchable, and paginated tables with customizable columns and a clean, component-driven API. Designed to keep table logic in PHP while staying easy to extend and style.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with composer require defstudio/wired-tables and confirm you're on Livewire 3 (v2.x of this package tracks the Livewire 3 line; the 0.x line is the Livewire 2 era). Laravel 10/11/12 and PHP 8.2+ — including the newly added PHP 8.4 support — are the target matrix.
  • Publish what you need before writing a table:
    php artisan vendor:publish --tag="wired-tables-config"
    php artisan vendor:publish --tag="wired-tables-views"   # only if you intend to override markup
    
    Then make sure Livewire's @livewireStyles / @livewireScripts (or the Vite livewire.js import) are in your layout — the inline-edit behaviour is Alpine-driven and silently does nothing without them.
  • Skim these three things in the repo before anything else: config/wired-tables.php (defaults for pagination, theme, editable behaviour), the WiredTable base class (lifecycle hooks, query handling), and the Column classes (the actual API surface you'll spend your day in).
  • First use case: take your most-requested "edit one field" admin screen — usually a Users or Products list where someone flips a status, tweaks a price, or fixes a typo in a name — and make it editable in place. You delete the edit page, the form request, and the redirect-back-with-flash. Start there, not with a 40-column ERP grid.

Implementation Patterns

  • One Livewire component per table, extending the package's table class. Columns are declared in a columns() method; each column is a fluent object (->searchable(), ->sortable(), ->editable(), ->format(...)). Keep the component thin — it's a view model, not a service.
  • Scope the query, always. Override the base query in the table class rather than in the Blade view:
    protected function query(): Builder
    {
        return User::query()->whereBelongsTo(auth()->user()->team));
    }
    
    This is the single most important pattern: every subsequent feature (search, sort, pagination, inline save) operates on whatever this returns, so tenancy and soft-delete rules belong here and nowhere else.
  • Treat editable columns as a write path, not a UI tweak. Inline edits hit model attributes directly on save. Wrap them:
    • add per-column validation rules before enabling ->editable();
    • use the table's update/save hooks to dispatch domain events, write audit rows, or bust caches — otherwise you get silent data changes with no trail;
    • guard privileged columns (roles, prices, is_admin) with an explicit check rather than trusting the UI to hide them.
  • Convention-over-configuration at team scale. Extract an abstract App\Tables\BaseTable with your house style (rows-per-page, date formatting, action column, empty state), then every concrete table is 20 lines. Push column definitions into small factory methods (StatusColumn::make()) once you have more than three tables doing the same thing.
  • Custom cell rendering via Blade views is the escape hatch for anything the built-in column types don't cover — badges, avatars, relationship chips. Publish the views only for the ones you actually change; prefer a per-column view so package upgrades stay cheap.
  • State in the query string. Search term, sort column/direction, filters, and page should be URL-bound so a filtered table is shareable and survives a refresh. Verify this explicitly — it's easy to end up with a table that resets every poll.
  • Pair with policies, not with @can in Blade. The Blade directive hides the control; the table class must still authorize the save. Pattern: a canEdit(User $record, string $column): bool method on the table class consulted by both the cell renderer and the save handler.
  • Testing: use Livewire::test(UsersTable::class) and assert on assertSee, assertSet, and call('...') for sort/filter transitions. Don't test the package's Alpine behaviour — test that your authorization, validation rules, and query scoping hold.

Gotchas and Tips

  • v0.2.43 → v2.0.0 is a major skip with no migration guide in the release notes. The changelog is 90% Dependabot bumps plus "php 8.4 support" — that is not a description of the breaking changes. Read the full compare diff (v0.2.43...v2.0.0) and the README/CHANGELOG on the tag before upgrading. Assume namespace moves, column API signature changes, and a Livewire 2→3 break; budget a real upgrade branch rather than a composer update.
  • Release notes ≠ documentation for this package. Every entry in this release is CI maintenance. Don't infer safety from a short changelog, and don't infer risk from a big version number — go to the source. Pin the version in composer.json ("^2.0") and read the diff once per minor.
  • Mass assignment. Inline editing writes to attributes, so anything in $fillable is potentially writable from a table cell. Audit $fillable/$guarded on every model you put behind an editable table; consider a dedicated $fillable for the table or explicit per-column assignment.
  • Livewire re-render churn. Tables rebuild on every keystroke in the search box. Add wire:model.debounce/->deferLoading()-style behaviour and check your query count — a table with relationship columns will N+1 hard. Eager-load relationships used in columns in the query() method.
  • Alpine conflicts are the #1 "it doesn't work" report. If you also load Alpine yourself, or you're on a custom Vite setup, the inline editor's x-data bindings can double-init. Check the browser console and the Livewire payload before blaming the package.
  • Pagination + filters resetting usually means table state lives in public properties that aren't bound to the query string, or you're remounting the component inside a parent re-render. Keep the table component as a top-level Livewire component, not nested in another one's loop.
  • Debugging technique: dump the resolved query from the table class, and inspect the Livewire network request to see exactly which method and payload the cell save sends. Most "it didn't save" bugs are validation failing silently — surface validation errors in the cell, not just the console.
  • Extension points worth knowing: custom column classes (extend the base column), per-column Blade views, published config for defaults and theming, and the table lifecycle hooks for save/delete. These four cover ~95% of real-world customisation without forking.
  • PHP 8.4 note: the package now supports it, but verify your other pinned deps (Livewire, your auth/tenancy packages) do too before bumping the runtime — a green package matrix doesn't mean a green app.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console