team-nifty-gmbh/tall-datatables
Livewire datatables for Laravel's TALL stack. Quickly add responsive, sortable, searchable, and filterable tables with pagination, customizable columns, bulk actions, query builder filters, row actions, and Blade-friendly views to admin panels and dashboards.
Install and wire up the TALL dependencies first. The package assumes Livewire 3, Alpine (bundled with Livewire — do not add a second Alpine CDN tag), and Tailwind. Install with composer require team-nifty-gmbh/tall-datatables, then publish anything it ships:
php artisan vendor:publish --tag=tall-datatables-config
php artisan vendor:publish --tag=tall-datatables-views # only if you plan to override markup
Look here first, in this order:
README.md — the package is small and the examples are the de-facto API docs.config/tall-datatables.php — it controls default per-page counts, view paths, and feature toggles.vendor/team-nifty-gmbh/tall-datatables/src/ — the base Livewire component and the column/filter classes are short and readable; when the README is thin, the source answers questions faster than guessing.resources/views in the package — this is what you'll override for custom cells.First use case: a read-only index page. Create a Livewire component that extends the package's datatable base, declare columns for a User model (name, email, created_at), and mount it in a Blade view:
{{-- resources/views/users/index.blade.php --}}
<x-app-layout>
<livewire:users-table />
</x-app-layout>
Make sure your tailwind.config.js content array includes ./vendor/team-nifty-gmbh/tall-datatables/resources/views/**/*.blade.php (and your own app/Livewire + resources/views), otherwise the table renders unstyled. Get sorting + search + pagination working on one flat table before touching filters or actions — that validates the whole Livewire/Alpine/Tailwind loop end to end.
Column definitions drive everything. The normal workflow is one Livewire component per table, with columns declared in a method and the query built in another. Keep both declarative so the table stays a thin view of the model layer:
protected function columns(): array
{
return [
TextColumn::make('name')->searchable()->sortable(),
TextColumn::make('email')->searchable(),
BadgeColumn::make('status')->colors(['active' => 'green', 'banned' => 'red']),
DateColumn::make('created_at')->sortable(),
ActionColumn::make(), // edit / delete buttons
];
}
Query scoping belongs in the builder, not the column. Pass an Eloquent builder and eager-load anything a column touches (->with('team')) so a 25-row page doesn't become 26 queries. Filter callbacks should mutate that same builder.
Filters: start with the built-ins, then go custom. Select and date-range filters cover most admin screens. For anything domain-specific (tenant, "only mine", soft-deleted), add a custom filter that receives the builder and applies a where. Remember filters should call resetPage() — otherwise users sit on page 7 of a 2-page result set.
Row actions and bulk actions. Use action columns for per-row edit/delete and the package's bulk-action hook for multi-select operations. Prefer dispatching an event ($this->dispatch('user-deleted')) over mutating sibling components directly; it keeps the table reusable across pages. Wrap destructive actions in wire:confirm or a confirmation modal.
Custom cell rendering. When a value needs more than a string (avatar + name, progress bar, status pill with tooltip), point the column at a small Blade partial rather than fighting the built-in formatters. This is the main extension point you'll use week to week, and it keeps formatting logic out of PHP.
Compose, don't fork. For a project with many tables, create your own App\Support\DataTable base extending the package's component, set house defaults there (per-page, empty-state text, action styling), and extend that everywhere. Upgrades then touch one file.
Test with Livewire's own harness. Livewire::test(UsersTable::class)->assertSee('[email protected]') plus assertions on set('search', ...) and call('sortBy', ...) catches most regressions without a browser. Add one Dusk/Pest browser test for the Alpine-dependent bits (dropdowns, bulk-select) since those don't run in the component test.
"text-{$color}-600"), get stripped. Add the vendor view path to content and safelist dynamic color/badge classes.composer update won't refresh them. Pin the version in composer.json, read the changelog before bumping, and diff your overrides against the new package views. With 14 stars and a fast-moving repo, treat minor versions as potentially breaking.sortable() on team.name typically needs a join or subquery in the builder; otherwise you get an SQL error or a silent no-op. Same for accessor/computed columns — search won't see them unless you supply a custom search callback.config/tall-datatables.php, run php artisan config:clear. In production with config:cache, the published config is baked in — deploy scripts that skip the rebuild will serve old defaults.wire:key and check that Alpine x-data IDs don't collide; two instances of the same table with shared filter state is a common bug source.withCount.Livewire::test() to isolate PHP from Alpine, then the browser console. Most "the table is empty" reports are a base query returning nothing — log the builder's toSql()/getBindings() before blaming the package.How can I help you explore Laravel packages today?