digital-creative/collapsible-resource-manager
A Filament plugin that organizes sidebar navigation into collapsible resource groups, keeping Laravel admin panels tidy as resources grow. Supports nested groups and remembers each user's open or closed state, making large apps with many resources easier to navigate.
Verify the platform before you write a line of code. The package description calls this a Filament plugin, but it is a Laravel Nova package — the v2.3.0 release notes explicitly say "Add support for Laravel Nova 5." If your admin panel is Filament, this is the wrong package and no amount of config will fix that. Confirm with:
composer show laravel/nova
If you see Nova 4.x or 5.x, you're in the right place. If you see Filament, stop.
Install and pin.
composer require digital-creative/collapsible-resource-manager:^2.3
Do not float the constraint. This is a single-maintainer package (~198 stars, 0 recorded dependents, one community PR carrying Nova 5 support). Pin to ~2.3 or exactly 2.3.0, and record the minimum in composer.json so the next developer doesn't accidentally downgrade below the Nova 5 floor.
Publish and clear.
php artisan vendor:publish --provider="DigitalCreative\CollapsibleResourceManager\CollapsibleResourceManagerServiceProvider"
php artisan config:clear
php artisan cache:clear
php artisan nova:publish
Re-run nova:publish after any Nova major bump — Nova 4 (Vue 2/Mix) and Nova 5 (Vue 3/Vite) do not share an asset pipeline, and stale published assets are a classic silent failure.
Where to look first. Two files own everything:
app/Providers/NovaServiceProvider.php — specifically tools() and mainMenu(). This is where you register the menu.Pick one as the source of truth on day one. Mixing them is the single most common source of "why isn't my group showing up" tickets (more in Gotchas).
First use case. Don't start with your whole menu. Pick the sidebar that actually hurts — 20+ resources across several roles — and define a single group with 3–4 resources. Verify it collapses, expands, and remembers state. Then roll the rest in.
A minimal fluent registration:
use DigitalCreative\CollapsibleResourceManager\CollapsibleResourceManager;
public function tools(): array
{
return [
CollapsibleResourceManager::make()
->navigation(fn ($nav) => $nav
->group('Content', fn ($g) => $g
->icon('document-text')
->resource(Post::class)
->resource(Page::class)
)
->group('People', fn ($g) => $g
->icon('users')
->resource(User::class)
->resource(Team::class)
)
),
];
}
Method names vary slightly across package versions — trust your pinned tag's README over any blog post, including this one. The structure is what matters.
Ship it behind a config flag that flips back to Nova's stock menu in one line. That revert is your safety net for every subsequent step.
Group by intent, not by model type. The most common failure is grouping by "kind" — all resources together, all tools together, all cards together. Users navigate by task. Prefer domain groups (Content, Billing, Ops, Settings) or workflow groups (Onboarding, Day-to-day, Admin). "Resources" is not a group; it's a technical accident.
Keep the tree two levels deep, maximum. The Nova sidebar is narrow. A group inside a group inside a group is a maze with no exit. If you find yourself wanting a third level, that's a signal you actually need Nova search enabled, or a second panel.
Order deliberately, per persona. Put the highest-frequency group first for each role. Ordering is typically insertion order; if you need explicit weights, use the package's ordering option rather than relying on PHP array key order, which is one refactor away from silently changing.
One dedicated place for definitions. Whether you go config-driven or provider-driven, keep the entire taxonomy in a single file so a menu change is a one-file diff. If your provider builds half the menu and the config builds the other half, you've created a scavenger hunt.
Prefer provider-driven registration for an actively-edited menu. Groups defined in tools() / mainMenu() are evaluated per request, so edits appear immediately. Config-driven groups sit behind config:cache and require a re-cache after every edit. While you're still shaping the taxonomy, that difference is real friction — the fluent route is faster to iterate on.
Own the group-level authorization yourself. Nova's authorize() on individual resources still applies to children, but group header visibility is typically declarative in this package. The result is an empty expander: a role sees "Billing" but clicking it reveals nothing. Two workable mitigations:
// Option A — build groups from the authenticated user's abilities
->navigation(function ($nav) use ($user) {
if ($user->can('viewAny', Invoice::class)) {
$nav->group('Billing', fn ($g) => $g->resource(Invoice::class));
}
return $nav;
})
// Option B — keep groups declarative, but diff the sidebar per role in QA after every policy change
Either is fine. Silently doing neither is not.
Keep the tool isolated from existing mainMenu() customization. Registering via tools() keeps CollapsibleResourceManager::make() from colliding with whatever you already do in mainMenu(). If you already customize mainMenu() and this package also touches it, verify which one wins — only one thing should own the sidebar.
Write one cheap test per role. Boot Nova as each role and assert the rendered group count and the first item in each group. It's a handful of lines and it catches policy regressions that nobody will catch visually, because nobody re-checks every role's sidebar after a permission change.
public function test_billing_group_visible_to_finance_role(): void
{
$this->actingAs(User::factory()->finance()->create());
$this->get('/nova')
->assertSee('Billing')
->assertSee('Invoices');
}
Day-to-day workflow.
Version-upgrade sequencing. Because v2.3.0 is the tag that added Nova 5 support, collapsible navigation is not a migration blocker — it carries forward. But "compatible on paper" is not "tested against your 40-resource set." Exercise it against your real menu before declaring the upgrade done.
Only one sidebar owner. This is the number-one integration failure. If another package or a bespoke Nova::mainMenu() override also touches the sidebar, expect duplicated menus or silent overrides where yours loses. Audit on install, and again after every new Nova extension lands.
Empty groups look broken to users. A role that can't see any resources in a group will still see the group header unless the package gates groups by child authorization — verify this per version rather than assuming. Either gate it yourself (see patterns above) or diff each role's rendered output.
Config caching will bite you. If you take the config-driven route and run config:cache (as you should in production), edits to group definitions won't appear until you re-cache. This is not a bug; it's config caching doing its job. It's also a good reason to prefer fluent registration while the menu is in flux.
Assert nothing about collapse state persistence you haven't verified. Assume expand/collapse state is browser-local (session or local storage) until you've confirmed otherwise in your install. If you need per-user persisted preferences across devices, plan a fork or a customization — don't expect it built-in.
Asset publishing across Nova majors. After any Nova major upgrade, re-run nova:publish and re-test in a browser. Published assets from Nova 4 do not carry into Nova 5's Vite pipeline. Treat v2.3.0 as "compatible on paper, freshly tested on Nova 5" until you've run it against your real resource set.
Pin the version, non-negotiable. Single maintainer, ~198 stars, zero recorded dependents. Use "digital-creative/collapsible-resource-manager": "~2.3" and keep the revert documented. MIT means vendoring or forking is a legitimate fallback — plan for that possibility rather than assuming the maintainer will be there through the next Nova major.
Debugging a missing resource — in this order:
authorize()?config:clear / config:cache after editing?mainMenu() override touching the sidebar?Work down the list. Each step rules out an entire category, and skipping ahead wastes time.
Blast radius is the menu, not the data. No migrations, no models, no queues, no PII. The worst case is a broken sidebar. That justifies moving fast — but because it renders on every admin page, still do a staging pass. A cosmetic layer that breaks every page is not low-impact for ops users mid-shift.
Extension points. Group → item → icon → order covers most real needs. Custom item types, badges, and non-resource links will pull you into the package internals. If navigation becomes a strategic differentiator, fork deliberately and own it — never patch vendor code inline, because the next composer update will erase your work silently.
Watch the description. The marketplace copy says Filament; the release notes say Nova. If a colleague forwards you this package for a Filament project, forward them this assessment instead.
How can I help you explore Laravel packages today?