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

Livewire Smart Table Laravel Package

tkaratug/livewire-smart-table

A Laravel Livewire package for building smart, interactive data tables with minimal code. It adds search, sorting, pagination, filters, and customizable columns and actions to Livewire components, speeding up admin panels and CRUD lists.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify the Livewire pairing first. Install with composer require tkaratug/livewire-smart-table. As of v1.5.0 the package declares Laravel 10 and PHP 8.2 support, so it will install cleanly on a modern-but-not-bleeding-edge stack. Before writing any table code, confirm which Livewire major version your app runs: v1.5.0 is a framework-compatibility bump, not a Livewire 3 port. On a Livewire 2 app you can proceed directly; on Livewire 3 (or Laravel 11+, whose constraints the release notes don't cover), install into a scratch branch, check composer why-not if resolution fails, and render one trivial table before committing to the dependency.

Publish and read the config. Run php artisan vendor:publish and look for the package's provider tag. The published config and Blade views are the real documentation for this package — with only ~92 stars there is very little community writing online, so the vendor-published views are your best reference for markup, styling hooks, and available slots.

Create your first table as a dedicated Livewire component. The intended shape is a component that owns the query and column definitions and renders the package's table view. Start with the smallest possible case: an admin index page listing User records with name, email, and created-at, sortable and searchable. Wire it into the page with <livewire:users-table /> and verify:

  1. Pagination advances.
  2. A search term narrows the set.
  3. Clicking a sortable header toggles direction.

If those three work, the integration is sound and everything else (filters, bulk actions, exports) is incremental configuration.

Where to look first when something breaks:

  • vendor/tkaratug/livewire-smart-table/src/ for the base component class and its traits — this is the actual API surface.
  • vendor/tkaratug/livewire-smart-table/resources/views/ for cell/header templates you can override.
  • config/ after publishing, for pagination sizes, default sort, and per-page options.

First real use case: replacing a hand-rolled @foreach table on an internal admin screen. That's where the package pays for itself — you get sorting, search, pagination, and bulk selection without maintaining a bespoke trait stack. Don't use it on public-facing, high-traffic listings until you've benchmarked it; the component keeps state in Livewire properties and round-trips per interaction.

Implementation Patterns

Component-per-table, not table-per-model. Create one Livewire component per screen rather than trying to make a generic configurable table. Column definitions, filter sets, and bulk actions diverge quickly between screens, and a shared "god table" becomes harder to reason about than three small components.

Define the query once, in a single method. Keep the base Eloquent builder in one place (typically a query() method or protected property) and let the package's sorting/searching mutate it. This gives you a single seam for authorization scoping:

protected function query()
{
    return User::query()
        ->when(! auth()->user()->isAdmin(), fn ($q) => $q->where('team_id', auth()->user()->team_id));
}

Apply tenancy/ownership constraints here, not in the render method — it guarantees every sort, search, and export path inherits them.

Treat column definitions as the contract. Mark only genuinely sortable columns as sortable, and only indexed/searchable columns as searchable. Derived columns (formatted currency, concatenated names, status badges) should render via a callback or a small Blade partial but should not be marked sortable unless you also supply custom sort logic — otherwise the package will emit an ORDER BY on a column that doesn't exist in the result set.

Eager-load for rendered columns. Anything a cell callback touches — $row->team->name, $row->roles->pluck('name') — needs with() in the base query. Livewire datatables are a classic N+1 amplifier because the render loop is hidden behind the package.

Filters belong in public properties with a reset hook. Bind filter inputs with wire:model to public props (e.g. $status, $dateFrom), apply them inside query(), and reset pagination whenever a filter changes. Without the reset, a user filtering from page 7 lands on an empty page and files a bug.

Bulk actions: select, confirm, authorize, act, clear. The standard flow is public $selected array bound to checkboxes, a confirm step for destructive actions, a per-record Gate::allows() check inside the loop (never trust the UI), the operation itself in a transaction, then $selected = [] and a $this->dispatchBrowserEvent/flash for feedback. Skip per-record authorization and you've built a mass-assignment vulnerability with a checkbox UI.

Exports are a separate concern from the table. If you add CSV export, build it from the same base query but without pagination and with a chunked cursor. Reusing the paginated result set is the most common export bug — users get 15 rows instead of 4,000.

Testing: use Livewire::test(UsersTable::class) and assert on set('search', 'foo') → assertSee(...), plus call('sortBy', 'email'). These tests catch the two most common regressions: a column rename breaking sort, and a filter silently ceasing to apply after a refactor.

Gotchas and Tips

Livewire version drift is still the headline risk — v1.5.0 did not fix it. The release only widened framework constraints (Laravel 10, PHP 8.2). The codebase still carries Livewire 2-era semantics ($listeners, emit, dispatchBrowserEvent), so on Livewire 3 expect to pin the package's major or fork/patch it. Budget a day for this before adopting; the compatibility bump makes install easier but does not remove the biggest hidden cost.

One release in ~two years; treat yourself as the maintainer. v1.5.0 is a good sign — the package isn't abandoned — but it is a narrow maintenance bump with no new features and no Livewire 3 work. Fork it into your own namespace or vendor it via a path/VCS repository if the app's lifespan exceeds a year. Don't build core product functionality on top of a low-cadence package without a vendoring strategy.

Check the composer ceiling against your Laravel version. The notes name Laravel 10 as the supported version; they say nothing about Laravel 11+. If your app is on a newer Laravel, composer require will fail on constraint resolution rather than at runtime — decide up front whether to pin the framework or take on a fork.

Searching relationship columns requires joins or whereHas. Free-text search typically compiles to LIKE against real columns. Searching team.name from a users table won't work without an explicit join or a whereHas clause added to your query — and if you join, watch for duplicate rows and ambiguous id columns (always qualify, e.g. users.id).

Reserved property names will bite you. Don't name public properties things Livewire or the package already use ($page, $search, $sort, $selected if the package defines them). Collisions produce silent state clobbering that's painful to trace. Check the base class's public properties before naming anything.

Pagination + wire:model = extra requests. Debounce filter inputs (wire:model.debounce.500ms) and avoid binding complex objects. Every keystroke round-trip re-runs your query; on a large table this is visible.

Debug the SQL, not the component. When results look wrong, temporarily add DB::listen(fn ($q) => logger($q->sql, $q->bindings)) or ->dd() the builder returned by your query() method. Nine times out of ten the bug is in the base query (a missing where, a stray orWhere breaking grouping), not in the package.

Custom cells: publish the views, don't edit vendor. Override cell rendering through the published Blade views or a callback, and keep the overrides in resources/views/vendor/livewire-smart-table/. Direct vendor edits vanish on composer update and are invisible in code review.

Config caching in production. If you publish config, run php artisan config:cache as part of deploy and re-run it after any config change. Stale cached config manifesting as "my per-page setting is ignored" is a recurring false-alarm.

Extension points, in order of preference: (1) override methods in your own component that extends the base class; (2) published Blade views; (3) package config; (4) patching the vendor source as a last resort, documented in your fork notes. Prefer (1) — it survives updates and keeps the diff reviewable.

If the adoption cost feels high, size the alternative honestly. For a single admin table on a Livewire 2 / Laravel 10 app, Livewire's own docs plus a 60-line trait stack is often cheaper than adopting, learning, and eventually forking a low-cadence package. Adopt this one where you have several tables to build quickly on a supported stack and can accept the maintenance tail.

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.
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
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource