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

Nova Collapsible Groups Laravel Package

tanmuhittin/nova-collapsible-groups

Laravel Nova package that organizes resource form fields into collapsible groups, turning long, complex forms into tidy sections users can expand and collapse. Configure group labels, default open or closed state, and styling with standard Nova fields—no custom components required.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a tiny asset-only package: it injects a JS/CSS bundle into Nova's layout that turns the sidebar group headings into collapsible toggles and (typically) remembers state in localStorage. No migrations, no config file, no publish step in the happy path.

composer require tanmuhittin/nova-collapsible-groups

Laravel package auto-discovery should register the provider (check extra.laravel.providers in the package's composer.json). If discovery is disabled in your app, add that provider class to config/app.php manually.

Where to look first, in this order:

  1. vendor/tanmuhittin/nova-collapsible-groups/src/ — the service provider. You want to see the Nova::script(...) / Nova::style(...) calls; they tell you exactly which file is injected and under what name.
  2. The JS file it points at (resources/js/... or dist/js/...). Read the selector it queries and the localStorage key format — this is the whole package, and you need to know both.
  3. composer.json of the package for the nova / laravel version constraints.

First use case: you already have public static $group = 'Reports'; on eight resources and the sidebar is a wall of headings. Install, hard-refresh, and the group headings become click targets that collapse their children and stay collapsed across navigations.

Verify with a hard reload and DevTools: Network tab should show the package asset loading with a 200, and the console should be clean. If nothing visually changes, the package is almost certainly not binding to your Nova version's DOM — see Gotchas.

Implementation Patterns

Group + order resources, then let the package do the collapsing. The package doesn't own grouping; Nova's $group/$priority does. Keep grouping declarative and consistent:

class ReportResource extends Resource
{
    public static $group = 'Reports';
    public static $priority = 10;      // order within the group
    public static $sort = true;        // alphabetical inside the group
}

The collapse unit is the group name, so treat $group strings as public API — they double as persistence keys in the browser.

Decide the default state deliberately. Most implementations of this pattern start expanded and only remember explicit user clicks. If you want a section collapsed for first-time users, the reliable route is forking the asset and seeding the default map rather than trying to pre-write localStorage keys from app code (the key format is an implementation detail you don't control).

Pair it with a documented sidebar convention. In app/Nova/NovaServiceProvider.php, or an App\Nova\Menu if you're on newer Nova, keep the ordered list of group names in one place so tooling/dashboards don't drift from $group values:

// Single source of truth used by resources and docs alike
final class NavigationGroups
{
    public const REPORTS = 'Reports';
    public const SETTINGS = 'Settings';
    public const BILLING = 'Billing';
}

Then public static $group = NavigationGroups::REPORTS; — renaming a group becomes a one-line change instead of a grep across 40 resources, and you can consciously accept the one-time loss of saved collapse state.

Custom tools and links are a mixed bag. Only Resources/$group-driven entries are reliably grouped. Tools, cards, and Nova::resourcesLinks-style custom entries render in their own section and may or may not participate. Test the specific entry types you use rather than assuming uniformity.

On Nova 4+, skip this package entirely. Nova 4's native Nova::mainMenu() with MenuSection and MenuGroup gives you collapsible groups as a first-class, supported feature:

Nova::mainMenu(function (Request $request) {
    return [
        MenuSection::make('Operations', [
            MenuGroup::make('Reports', [/* MenuItems */]),
            MenuGroup::make('Settings', [/* MenuItems */]),
        ])->icon('cog'),
    ];
});

If you're on Nova 3, a 20–30 line resources/js/tool.js bound to the current sidebar DOM is less risk than depending on a 2019 asset bundle.

Gotchas and Tips

  • Version compatibility is the whole risk. Last release 2019-03-18 puts it in the Nova 2 era. Nova 3 reworked the sidebar markup and Nova 4 rewrote the frontend. On modern Nova the failure mode is silent: the script loads, the selector matches nothing, nothing breaks and nothing collapses.
  • Asset caching will waste an hour of your life. After install or upgrade: php artisan optimize:clear, republish Nova assets if you've ever customized public/vendor/nova, then hard-reload with DevTools "Disable cache" checked. Old bundles surviving in public/vendor are the usual culprit.
  • Script ordering matters. If another package also decorates the sidebar, handlers can bind before the DOM they expect exists. Symptom in the console: Cannot read properties of null (reading 'querySelectorAll') or similar from the injected bundle. Resolve by controlling provider registration order, or fork.
  • Renaming a group orphans state. The localStorage key is derived from the group name; rename $group and users get a stale entry plus a fresh expanded section. Additive on disk, confusing in behavior.
  • Ungrouped resources are untouched. Anything with $group = null renders as a plain link, so a "collapsible groups" demo can look broken if half your resources were never grouped.
  • Mobile drawer and re-renders. Sidebar state can fail to reapply after the responsive drawer is toggled or after Nova re-renders navigation. Test on a narrow viewport before shipping to a team that works on laptops.
  • Zero dependents + no maintained releases = vendored code, not a dependency. MIT license means the pragmatic move is to copy the ~50 lines of JS into your own resources/js and register it with Nova::script() yourself. You keep the behavior, you own the fix when Nova bumps its DOM.
  • Debugging checklist: confirm the <script> tag is in the rendered page source (not just the network panel), inspect localStorage for the expected keys after a click, and toggle the DOM node directly in Elements to prove the CSS half works independently of the JS half.
  • Don't rely on it for access control. Collapsing is cosmetic; a collapsed group's links are still in the DOM and still routable. Gate resources with authorizedToViewAny as usual.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin