sprigs/expandable-row
Sprigs Expandable Row is a Laravel/PHP package that adds collapsible expandable rows to tables and list views, letting users reveal extra details inline without navigating away. A small, focused tool for cleaner, more informative admin panels and data-heavy interfaces.
SPRIGS/nova-expandable-row, so confirm the actual Packagist name before committing — composer require sprigs/expandable-row and check composer show sprigs/expandable-row resolves. A 4-star, 0-dependent package is a small blast radius but also means the README may be the only documentation.src/ before touching a real Resource. Look for the service provider (asset registration / Nova::script / Nova::style), then the main class that implements the row behaviour. With packages this small, the source is the docs — usually one trait or one field class you attach to a Resource.composer.json for a extra.laravel.providers block. If absent, add the provider to config/app.php or NovaServiceProvider::register().publishes(). Run php artisan vendor:publish --provider="Sprigs\\ExpandableRow\\..." (grep the provider for the exact namespace) and then php artisan nova:publish.?perPage=25.Opt-in per Resource, not globally. Resist applying it to every Resource at once. A common shape is a base App\Nova\Resource that pulls in the package's trait/behaviour, with individual Resources overriding the "which fields go in the expanded row" method. That keeps the surface area to one class you can revert.
Curate the expanded payload deliberately. The modal renders the full detail view; an expanded row renders inside a <tr>. Restrict the expanded content to scalar fields — Text, DateTime, Badge, Boolean — and leave File, KeyValue, Textarea with long content, and any field with its own heavy sub-request in the modal. If the package exposes a method like expandableFields() or a fields() override on the row class, that is your allowlist.
Eager-load what the row needs. The biggest practical win of killing the modal is avoiding one extra request per row — don't reintroduce it. If the expanded row touches a relation, add it to indexQuery():
public static function indexQuery(NovaRequest $request, $query)
{
return $query->with(['customer:id,name', 'assignee:id,name']);
}
Watch Telescope/Debugbar on the index page; an N+1 here multiplies by page size.
Compose with the existing table rather than replacing it. Keep searchable/sortable columns in the normal index columns and treat the expanded area as supplementary detail. This preserves filters, sorting and the perPage selector, which a full-view replacement usually breaks.
Wire it into an existing workflow, not a new one. The good use case is a triage screen: filters narrow to 20 rows, the developer/ops user expands 3, reads the detail, clicks an action (Action / inline button) without navigating. Pair it with row actions so expansion replaces "click View → do thing → back".
Treat it as a UI layer over viewAny. Whatever the package does, verify it doesn't call the detail endpoint. Test as a user with a policy that denies view but allows viewAny — the expanded row should either hide fields or not render at all.
packages/ with a path repository if you intend to keep it long-term — that's usually cheaper than waiting for a PR.view policy or expose fields the modal would have gated. Grep the source for authorize / canSee. Field-level canSee() on individual fields is usually still respected — confirm.Textarea collapses, Stack/Line may need explicit widths, Badge and Status are fine. Test in dark mode and at narrow widths — a <tr> has far less room than a modal.composer update, run php artisan nova:publish, php artisan view:clear, and hard-reload — Nova's compiled assets are a classic false-negative when debugging.perPage should come down (25 → 15) for wide Resources.app/Nova/ rather than patching vendor/.How can I help you explore Laravel packages today?