unnathianalytics/laragrid
Excel-style keyboard-first datagrid for Laravel and Livewire. Configure sorting, filtering, editable cells, and formulas entirely in your component class—no Blade wiring, npm, or JavaScript. Server-side validated over renderless RPCs.
Install and confirm the environment. composer require unnathianalytics/laragrid is the whole install — no npm, no published assets, no build step. Confirm you're on a Livewire version the package supports (check composer.json constraints, not just the README badge) before you plan anything around it.
Look at the live demo first, then the source. grid.laravel.cloud is the fastest way to understand what "Excel-style, keyboard-first" actually means in practice — arrow-key navigation, Enter/Tab to commit, shift-select, footer sums. Then read src/ for the base component/trait and the column classes. The README documents the happy path; the real API surface (column types, filter types, hooks) lives in those two directories.
Your first use case should be the readonly server-side grid. It's the lowest-risk entry point: point it at an Eloquent query or builder, declare columns, and you get sorting, global search, filters, pagination, selection, and footer totals through the package's whitelisted pipeline. Nothing is mutated, so a misconfiguration costs you a broken table, not corrupted data.
The shape of the work is roughly:
php artisan make:livewire ResortsTablemount() (or a columns() method the package expects).Set expectations with your team up front. With 60 stars and zero dependents, this is a young package. Budget for reading source when the README runs out, and pin the exact version in composer.json — a minor bump on a 0.x/1.x package can move the configuration API.
Pick the mode deliberately, per screen. The three modes are not tiers of the same thing:
Keep the component class as the single source of truth. The core value proposition is that grid config lives in PHP. Protect that:
protected function columns(): array and call it from mount(). A 40-line mount() full of chained column calls is the failure mode this package invites.AccountsTable, ContactsTable extend AbstractLedgerTable). This is the natural extension point and it's cheaper than fighting the package's defaults per screen.Lean on server-side authority for editable grids. The architecture exists so Livewire never touches a row: the body sits in a wire:ignore region and the engine owns the DOM. That means:
Compose selection and aggregates instead of post-processing. Bulk selection plus footer totals are the reason operators trust these grids. Wire them to the package's built-in selection rather than re-querying in the Blade layer — otherwise "select all across pages" and your footer will disagree.
Use the search/filter declarations as your public contract. Because the pipeline is whitelisted and fail-closed, the set of sortable columns and filterable fields you declare is the API. Document it, and mirror it in tests.
Integration tip: isolate one grid per Livewire component. Don't mount three grids in one component and expect independent state. One component, one grid, one data source.
wire:ignore is the number one thing that will confuse you. Because Livewire never morphs rows, parent-state changes won't visually update the grid. If a filter, a permission change, or a related record doesn't reflect, the fix is usually an explicit refresh/re-key on the grid component, not a wire:model tweak. Learn the refresh path on day one.
Fail-closed whitelists fail silently. A column that isn't declared sortable just won't sort — no exception, no console warning. Same for filters and searchable fields. When "sorting doesn't work," check the declaration before you check the query. Build a habit of adding a smoke test that hits each declared sort and filter.
Sort keys and filter keys are not always column names. Aliased/computed columns frequently need an explicit key mapped to a real database column. If a sort throws or is ignored, this mapping is the first suspect — and it's also your SQL-injection boundary, so never pass user input through unmapped.
Editable grid commits are network round-trips. Fast typists will outrun the server. Understand whether the engine batches, debounces, or fires per cell before you promise a UX. Pair this with clear error surfacing: validation failures need to land back on the cell, and that depends on your error-key format matching the package's expectations.
Watch for N+1 in formatters. A badge or money formatter that touches $row->relation->name will explode on a 100-row page. Eager-load in the base query — the package can't do it for you, and the resulting slowness will look like a grid performance problem when it's a query problem.
Selection across pages is a classic trap. Verify whether "select all" means the current page or the whole filtered set, and make sure your bulk action and footer totals agree with that definition. Get this wrong in an accounting context and you ship a data-loss bug.
Customization has a ceiling. PHP-side column renderers and configuration are the supported extension points. Anything requiring custom JS, a different cell editor, or a non-standard keyboard binding means forking or vendoring. Check for those requirements during evaluation, not in week three.
Test through Livewire, not the browser. Livewire::test(ResortsTable::class) plus calls to the package's RPC methods covers sorting, filtering, and edit validation without touching the JS engine. That's the highest-value test surface — and the only realistic way to catch whitelist regressions.
Pin the version and watch the changelog. Low adoption means fewer people hitting the edge cases before you do. Pin exactly, review diffs on upgrade, and keep a short internal note of which package internals you depend on so an API change is a known cost rather than a surprise.
How can I help you explore Laravel packages today?