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 Powergrid Laravel Package

power-components/livewire-powergrid

Livewire PowerGrid is a Laravel Livewire package for building modern, customizable data tables. It ships with sorting, global search, column filters, bulk actions, inline editing, and data export ready out of the box, plus an Artisan scaffold to generate table components fast.

View on GitHub
Deep Wiki
Context7

Getting Started

Pick your major version first — this is the single most important decision. PowerGrid is mid-flight between two incompatible lines:

6.x (stable) 7.x (beta, currently 7.0.0-beta.8)
PHP 8.1+/8.2+ 8.3+
Laravel 10/11/12 12/13
Livewire 2/3 4.0+
Tailwind 3.x 4.x
Themes Tailwind, DaisyUI, Bootstrap 5 Tailwind, DaisyUI, Flux (Bootstrap dropped)

If your app is on Livewire 3 / Tailwind 3, install power-components/livewire-powergrid:^6.11 and ignore the v7 docs. Only reach for ^7.0@beta on a greenfield Livewire 4 / Tailwind 4 app — and read UPGRADE.md on the 7.x branch before committing, because PowerGrid::lazy() / LazyManager, window.pgActions, and the Bootstrap theme are gone with no shims.

composer require power-components/livewire-powergrid
php artisan vendor:publish --tag=livewire-powergrid-config   # config/livewire-powergrid.php
php artisan vendor:publish --tag=livewire-powergrid-assets   # theme CSS/JS
php artisan powergrid:create DishTable --model=App\\Models\\Dish

Where to look first, in order:

  1. https://livewire-powergrid.com/get-started/install.html — the README deliberately punts the real install steps (Tailwind content paths, @powergridScripts, layout @livewireStyles) to the docs. Do not try to infer install from the README.
  2. The file app/Livewire/DishTable.php generated by powergrid:create. It is a working scaffold with commented-out examples for filters, actions, and bulk actions. Uncomment-to-learn is the intended workflow and is faster than the docs for day-to-day work.
  3. config/livewire-powergrid.php — theme selection, per-page options, export disk/directory, cache TTLs, and (v7) escape_closure_fields.

First use case: replace a hand-rolled <table> in an admin panel. You get sorting, global search, pagination, and per-column filters "for free" the moment datasource() returns rows and columns() returns column objects. Budget ~20 minutes from composer require to a themed, searchable list — most of that is Tailwind asset wiring, not PHP.

If you use Laravel Boost, run php artisan boost:install (or boost:update --discover). PowerGrid ships Agent Skills that teach Claude Code/Cursor the v7 struct() theme API and plugin scaffolding — genuinely useful here because v7's plugin system is under-documented relative to its surface area.

Implementation Patterns

Component anatomy

Every table is a Livewire component extending PowerGridComponent with four near-mandatory methods plus optional chrome:

public function datasource(): Builder
{
    return Dish::query()->with('category:id,name');   // eager load = your N+1 insurance
}

public function relationSearch(): array
{
    return ['category' => ['name']];                  // makes global search hit relations
}

public function fields(): PowerGridFields
{
    return PowerGrid::fields()
        ->add('id')
        ->add('name')
        ->add('price', fn (Dish $d) => number_format($d->price / 100, 2))
        ->add('created_at_formatted', fn (Dish $d) => $d->created_at->format('d/m/Y'));
}

public function columns(): array
{
    return [
        Column::add()->title('ID')->field('id')->sortable(),
        Column::add()->title('Dish')->field('name')->sortable()->searchable(),
        Column::add()->title('Price')->field('price')->sortable()->withSum('total'),
        Column::action('Action'),
    ];
}

The split between fields() (render values, including computed/derived ones) and columns() (behaviour + presentation) is the core mental model. Anything you compute in fields() is not sortable unless you also add it to datasource() as a selected alias or use searchableRaw / sortableRaw with real SQL.

Note that fields() closure output is HTML-escaped by default. Since v7.0.0-beta.8 an escape_closure_fields key in config/livewire-powergrid.php controls this — flip it off only when you deliberately render trusted markup (badges, links) from a closure, and prefer returning HtmlString for the one column that needs it over disabling escaping globally.

Data sources

Three shapes, in increasing order of control:

  • Eloquent builder — default, supports sortable() on real columns and relationSearch().
  • Query builder / DB::table() — same API, loses model accessors in fields() closures.
  • Collection / array — fine for API-backed or small fixed lists, but sorting/filtering/pagination are client-side-ish and summaries behave differently. Reach for this only when you truly cannot express the source as SQL.

For multi-tenant or permission-scoped tables, apply the scope inside datasource() — never in columns() or filters(). Everything downstream (search, filters, export, bulk actions) inherits the builder, so scoping there is the one place that can't be bypassed by a crafted Livewire payload.

Filters

filters() returns an array of filter objects; each declares a field() that must match a column or a searchableRaw alias. Pattern that scales well: put one filter per high-cardinality dimension only (category, status, date range) and leave full-text to the global search box. Every filter is one more round-trip on each keystroke unless you set ->debounce() sensibly.

Actions, buttons, and conditional rules

Actions are declared per-column and rendered server-side in v7 (in 6.x they were JS-driven — this is the biggest behavioural difference when porting). The idiom that keeps you out of trouble is Rule:: for visibility/enabling, because rules evaluate on the server:

Button::add('edit')->slot('edit')->route('dishes.edit', ['dish' => 'id']),

Button::add('delete')->slot('delete')
    ->click('confirmDelete')                       // component method
    ->rules([
        Rule::when(fn (Dish $d) => $d->orders_count === 0, 'can_delete'),
    ]),

Because Rule closures are serialized into the Livewire component's state, keep them pure and cheap — capture scalars via use, avoid touching auth()->user() inside the closure if you can hoist the check to datasource(). Non-serializable captures surface as confusing "closure could not be serialized" errors at render time, not at declaration time.

Bulk actions and export

Bulk actions are the same Button/Action classes with ->confirm() and access to the selected-key array. For export:

  • CSV/XLSX goes through OpenSpout; add WithExport / Exportable as your version's trait requires.
  • For anything over a few thousand rows switch to queue export and poll for readiness — synchronous export will blow the request timeout and is the #1 complaint in the issue tracker.
  • Column selection for export is independent of what's on screen; define exportColumns() explicitly, otherwise hidden/computed columns leak into the file.

Inline editing, toggles, date pickers

These are plugins, not column flags, in v7. Enable, then mark columns:

Column::add()->title('Active')->field('active')->toggleable();

public function onUpdatedToggleable(int|string $id, string $field, string $value): void
{
    Dish::query()->where('id', $id)->update([$field => $value]);
}

The onUpdated{Plugin} hook is optional — without it the plugin's dispatch event is routed to pgPluginListener and you get no persistence. This is the most common "the toggle flips but nothing saves" bug. Same shape for onUpdatedEditable.

Multi-table pages and Hot Zones

Give every component a distinct tableName (it defaults to a class-based slug; override it if you have two tables from the same model). In v7 the pg-tbody / pg-pagination / pg-filters hot zones mean only the region that changed gets patched — big win, but it also means a wire:key collision or a missing @powergridScripts in one partial produces silent no-ops rather than errors.

Theming

Pre-v7 you pick a theme in config and override Blade views. In v7 you call struct() on the theme and get a fluent builder returning tokens; config('livewire-powergrid.theme_overrides') is merged last, so a no-code override always beats a plugin's themeTokens() and the theme's own defaults. Order of precedence worth memorising: theme section methods → plugin themeTokens() → theme_overrides.

Gotchas and Tips

  • Tailwind won't style the table until you add the vendor path. Add ./vendor/power-components/livewire-powergrid/resources/views/**/*.blade.php (v6) or the v7 theme path to content in tailwind.config.js / the v4 @source directive. Symptom: unstyled pagination and a very wide, borderless table. This is the most common install failure.
  • Config caching bites on deploy. php artisan config:cache after changing theme, per_page, or escape_closure_fields — and remember published assets are versioned; re-publish or your CSS drifts from the Blade.
  • Escaping in fields() closures. Closure output is escaped by default, so fn ($d) => "<span class='badge'>{$d->status}</span>" renders as literal text. In v7 you can set escape_closure_fields => false globally in config, but prefer returning HtmlString for the specific column — a global opt-out quietly turns every user-supplied field value into an XSS vector.
  • Filtering/sorting on computed fields silently does nothing. If a fields() closure isn't backed by SQL, ->sortable() renders an arrow that doesn't sort. Fix with sortableRaw('(price / 100)') or accept it and drop the arrow.
  • datasource() scoping is your security boundary. Filters arrive from the client; the base builder does not. Any row-level permission check belongs in the builder or in Rule:: — never in Blade.
  • Beware SELECT * on wide tables. PowerGrid needs every sortable/searchable/filterable column present. Explicitly select() what the table shows; on a 60-column table this alone is often a 3–5× win, and it makes the pg-tbody hot-zone diffs cheaper too.
  • Export + big data = queue. Configure disk and directory for batch exports (configurable since v7 beta) and always Storage::disk(...)->exists()-check before offering the download link.
  • Scout support changes datasource() semantics. With Scout, sorting and non-search filters have to be re-applied to the collection afterwards; don't expect sortable() to work identically to the Eloquent path. Test the search path explicitly.
  • Version pinning in composer.json is a real safeguard. ^7.0 will not resolve to betas by default, which is what you want in production; conversely ^6.11 will happily pull 6.11.x bugfixes (e.g. the row-actions-after-pagination fix) that you do want.
  • Track v7 beta numbers when you're on the beta. 7.0.0-beta.8 is a single small feature (escape_closure_fields); betas drop frequently, so re-read release notes per bump and don't assume config keys added mid-beta exist in the tag you pinned.
  • Debugging recipe: ->dd() on the component dumps the builder; DB::listen() around the render shows exactly which filters fired; ->queryString() in v7 gives you the SQL as a string. Start there before opening an issue.
  • Extension point for one-off UI: don't fork the theme. Register a plugin implementing handles() / render() and contribute themeTokens() — that's the supported seam, and it survives minor upgrades. theme_view() with a powergrid-plugins:: fallback is the pattern internal plugins use for theme-specific markup.
  • Read UPGRADE.md before any 6→7 move. Removed: LazyManager / "load more", window.pgActions, PowerGrid::lazy(), Pulse integration, CDN fallbacks. The struct() rewrite touches every custom theme you own.
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.
sanjid29/laravel-starter-core
vanda/core
mimon/kafka-logger
webup/laravel-helium
hadiabedzadeh/laravel-journey-builder
shadowbane/laravel-gelf-logger
devloopsnet/bagisto-mastercard
sunxyw/laravel-admin
voxyfy/anadolupay
emild/laravel-metable
timedoor/baskito
larva/tencentcloud-sdk-php
jund-fauz/filament-kompact
atannex/panels
sahanranasingha/sri-lanka-holidays
alan2k2/cna-framework
moztopia/access
itorgov/laravel-forge-bot
backstage/laravel-ok
mr-swapan/applinks