ibrahimbougaoua/filament-menu-scroll-fix
A lightweight Filament plugin that resolves sidebar and navigation menu scrolling issues, keeping long menus usable and accessible in Laravel admin panels. Simple to install and designed as a drop-in fix for affected Filament layouts.
Installation
composer require ibrahimbougaoua/filament-menu-scroll-fix
Requires Filament v3 (built and released around the v3 era). Check composer.json in the repo for the exact filament/filament constraint before installing into a v4 app.
Where to look first
composer.json → look at extra.laravel.providers and extra.filament keys. Small packages like this usually auto-register a ServiceProvider that either registers a Filament plugin class or injects a JS/CSS asset through FilamentAsset::register().src/ for either:
...Plugin.php (implements Filament\Contracts\Plugin) — you must register it manually, or...ServiceProvider.php that calls FilamentAsset::register(...) — nothing to register, it "just works" once the panel is rendered.resources/js and resources/css — this is a UX fix, so the actual behavior lives there.First use case
Create (or use) a panel with enough navigation items to overflow the sidebar — e.g. 15+ resources. Then:
If the behavior does not change, jump straight to the Gotchas section — the most common cause is a stale asset build.
Minimal sanity check after install:
php artisan filament:assets
php artisan optimize:clear
Then hard-refresh the browser (the JS is cached aggressively).
Pattern 1 — zero-config panels
If the package auto-registers its service provider (the expected case for a package this small), there is nothing to wire up. Every Filament panel in the app will pick up the injected asset. This is the intended "install and forget" workflow.
Pattern 2 — explicit plugin registration
If src/ contains a plugin class, register it inside each panel you want fixed:
use IbrahimBougaoua\FilamentMenuScrollFix\FilamentMenuScrollFixPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
// ... other config
->plugin(FilamentMenuScrollFixPlugin::make());
}
Only register it on panels where it matters (e.g. the admin panel with a long nav), not on small tenant panels where the extra asset is unnecessary surface area.
Pattern 3 — per-panel opt-in via panel scoping
Because it is a frontend asset, the fix applies to the whole panel. If you have multiple panels with different sidebars (top navigation vs. collapsible), verify the fix on each one — the scroll container differs between Filament's sidebar layouts, and a fix tuned for the "sidebar collapsed" state does not always behave on the "top navigation" layout.
Pattern 4 — pairing with custom themes
If you use a custom Filament theme (via php artisan filament:theme), the injected JS is loaded after the theme CSS, so scroll/focus behavior is preserved. But the blade view that mounts the script must be reachable from your theme's layout — check the package's view path in src/.../resources/views and make sure you did not overwrite the layout view in your theme without re-including @filamentScripts / @livewireScripts.
Pattern 5 — CI / deploy pipeline
Treat it like any Filament asset package in your deploy step so the compiled JS is present on the server:
composer install --no-dev --optimize-autoloader
php artisan filament:assets
php artisan optimize
Skipping filament:assets is the single most common reason the fix "works locally but not in production."
Pattern 6 — testing the regression
Manually test three flows before shipping:
Ctrl/Cmd-click (open in new tab) → no unexpected scroll reset on the current tab (this path bypasses Livewire navigation, so it is where JS handlers most often misfire).The asset is cached — clear it aggressively.
php artisan filament:assets plus a hard refresh. If it still does not appear, inspect <head> for the package's script tag and confirm public/js/ibrahimbougaoua/... (or public/vendor/...) exists. If the directory is missing, the package's composer.json post-autoload-dump hook may not have run — re-run composer dump-autoload.
Filament version drift.
This package targets Filament v3. Registering its plugin class on a v4 panel will silently do nothing or throw a TypeError on the boot method signature. Pin it in composer.json if you are on v3 and upgrade-block it during the v4 migration.
Livewire SPA mode conflicts.
If you have another package that hooks livewire:navigated or wire:navigate, both handlers fire. Order matters. If scroll-restoration stops working, temporarily remove other JS packages to isolate — do not debug both at once.
Alpine.js re-init.
Filament uses Alpine (x-data) heavily for the sidebar. If your own code disables the sidebar's x-data or resets $el.scrollTop, the fix will be undone. Any x-init that touches the nav container should be removed before reporting a bug.
Custom scroll containers.
If you wrapped the sidebar in a custom overflow-y: auto element or a min-h-screen wrapper, the scroll element is no longer the one the package expects. The fix targets Filament's default nav scroll container. Verify with devtools that the element retaining scroll is the one you actually styled — otherwise the "bug is back" even though the package works.
Do not rely on it for focus accessibility.
The package names itself a "fix," but the description implies focus is set on the selected item. Do not assume it satisfies full WCAG focus management (visible focus ring, aria-current). Pair it with your own :focus-visible styling if you care about keyboard users — and test with Tab navigation, not just clicks.
Debugging: enable Filament's asset debug in local.
Add ->viteTheme(...) only if you've built assets; otherwise just clear caches. For deep debugging, open DevTools → Sources → search the injected script for scrollTop, add a breakpoint, and step through on a nav click. Also watch the Network tab to confirm the SPA request is a Livewire navigation (X-Livewire: true) rather than a full page load — full page loads always reset scroll and look like a package failure.
Extension points.
There is essentially no configuration surface for a package this size. The realistic extension points are:
publishes() entry for the view/asset, letting you fork the script locally (look for vendor:publish --tag=... in the service provider).->make() variants (e.g. per-panel enable/disable) — you can subclass the package's plugin and override boot().When to just remove it.
If your Filament nav is short (under ~10 items) and fits without a scrollbar, the package provides no benefit and adds a script to every page. Uninstall and move on — it is a targeted UX patch, not an architectural dependency.
How can I help you explore Laravel packages today?