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

Filament Panel Switch Laravel Package

rupadana/filament-panel-switch

A Filament plugin that adds a panel switcher to your admin interface, letting users move between registered Filament panels without manually editing URLs. Useful for apps with separate admin, staff, or tenant panels. Install via Composer and register the plugin on your panel.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal path from zero to a working panel switcher:

composer require rupadana/filament-panel-switch
php artisan vendor:publish --tag="filament-panel-switch-config"

Register the plugin on the panel(s) that should show the switcher, in the panel provider's panel() method:

public function panel(Panel $panel): Panel
{
    return $panel
        ->id('admin')
        // ...
        ->plugin(\Rupadana\PanelSwitch\PanelSwitchPlugin::make()); // copy the exact FQCN from the README
}

Then make sure the panels you want to switch to actually exist and are reachable by the same user. The single most important precondition is that all panels use the same auth guard (or that the user is valid on each guard) — otherwise "switching" is really "being logged out".

Where to look first

  1. The published config file (config/filament-panel-switch.php) — this is where the panel list, labels, icons and ordering live. Read it before touching any PHP.
  2. The plugin class itself — check which fluent setters exist in this major version rather than assuming v2 API.
  3. The README's upgrade/breaking-changes section for v3.0.0. The release notes for v3.0.0 only link to the commit list, so treat the README and composer.json constraints as the authoritative source.

First use case: a two-panel app (admin + app) where staff need to hop between a back-office panel and the end-user-facing panel without re-authenticating. Add the plugin to both panels, confirm both appear in the dropdown, and verify the switch preserves the session.

Implementation Patterns

Register only on source panels. If a panel should never be a jumping-off point (e.g. a public marketing panel), simply don't register the plugin there. Registration is per-panel, not global — this is the primary lever for controlling blast radius.

Config-driven registry, not hardcoded links. Keep the panel list in the published config (id, label, icon, sort, optional color) rather than building your own ->userMenuItems() entries with url(). You get consistent ordering, and a single place to add a panel later. For anything dynamic (per-tenant panels, per-role visibility), prefer wrapping the config with your own service provider merge rather than editing the vendor file.

Gate visibility per user. Pair the plugin with your existing canAccessPanel() logic on each target panel. The switcher does not grant access — it only links. If a user shouldn't see a panel, hide the item (via config callback or plugin condition if exposed) and keep canAccessPanel() honest; the two should agree, otherwise users get a menu entry that 403s.

Share the guard, share the session. For multi-panel setups, standardise on one guard (->authGuard('web')) across panels unless you have a genuine reason not to. Document the guard matrix in the panel providers — this is the thing that breaks subtly in staging but not locally.

Combine with tenancy deliberately. If panels use different tenancy configurations (tenant(..., slugAttribute: 'slug') vs none), the generated URLs differ in shape. Test switching while a tenant is active in the source panel; the target panel may need a tenant context it doesn't have. Where tenancy differs, treat the switcher as "go to panel root" rather than "go to the equivalent page".

Keep it out of the critical path. The switcher is navigation sugar. Don't build redirect-after-login or authorization flows on top of it — use Filament's ->login() / ->registration() redirects and panel canAccessPanel() for that.

Test the round trip. A small Pest/PHPUnit test that hits each panel's root URL as an authenticated user is worth more than any amount of manual clicking, because it catches guard and route-name regressions introduced by unrelated panel changes.

Gotchas and Tips

  • v3.0.0 is a major bump with a one-line changelog. Before upgrading, diff composer.json (Filament constraint, PHP constraint) and grep your panel providers for the plugin FQCN and any fluent calls you use. Assume the plugin namespace, config keys, or registration signature may have changed — verify against the README rather than the release notes. Pin the version and upgrade in a branch.
  • Config caching. After editing the config or adding a panel: php artisan config:clear and, if you use Filament's optimisations, re-run your filament:optimize / optimize:clear cycle. Stale cached config is the #1 cause of "the new panel doesn't show up".
  • Route caching + panel registration. Panels are registered in service providers; if a panel provider is conditionally registered (e.g. only in non-production), the switcher will silently lose entries. Check php artisan route:list for the panel's routes.
  • Same guard or you're logging out. Different guards on source and target panels mean the user arrives unauthenticated and gets bounced to the target's login. There is no cross-guard magic here.
  • canAccessPanel() still wins. A visible menu item that leads to a 403 is a UX bug, not a security bug — but it looks like one to users. Keep visibility and access checks in sync.
  • SPA / wire:navigate. If you enable Filament's SPA mode, cross-panel links point at different Livewire roots. If switching behaves oddly (stale page, double render), force a full page load for the switcher links.
  • Tenant-prefixed URLs. With tenancy enabled, the switcher's target URL may not carry a tenant slug. Expect to land on the target panel's tenant-selection or dashboard rather than deep-linking.
  • Extension point. If the plugin's config doesn't cover your case, the cleanest override is a small custom Filament render hook or userMenuItems entry that reuses the plugin's panel list from config — keep your own copy of the URL-building logic in one helper so future major upgrades touch one file.
  • Debugging checklist when the dropdown is empty or wrong: (1) is the plugin registered on this panel, (2) does the config list contain the other panel's id, (3) are both panels registered in bootstrap/providers.php / the panel provider list, (4) is config cached, (5) does the user pass canAccessPanel() on the target.
  • Don't fork it for small tweaks. Labels and icons belong in config; visibility belongs in your own access rules. Reserve a fork for genuine breakage, since major versions here move fast and a fork will rot quickly.
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.
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen