A headless sidebar package for Laravel that allows you to create and manage sidebars with full customization support.
Originally forked from SpartnerNL/Laravel-Sidebar.
Require this package in your project:
composer require shopper/sidebarPublish the config file:
php artisan vendor:publish --provider="Shopper\Sidebar\SidebarServiceProvider" --tag="sidebar-config"Publish the views (optional, for customization):
php artisan vendor:publish --provider="Shopper\Sidebar\SidebarServiceProvider" --tag="sidebar-views"Add the middleware to your route group or bootstrap/app.php:
// In a route group
Route::middleware(['web', \Shopper\Sidebar\Middleware\ResolveSidebars::class])
->group(function () {
// Your routes
});
// Or in bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
$middleware->appendToGroup('web', [
\Shopper\Sidebar\Middleware\ResolveSidebars::class,
]);
})The config file (config/sidebar.php) allows you to customize:
return [
// Caching method: null, 'static', or 'user-based'
'cache' => [
'method' => null,
'duration' => 1440,
],
// Sidebar dimensions (CSS values)
'width' => '16.5rem',
'collapsed_width' => '4.5rem',
// Responsive breakpoint (pixels)
'breakpoint' => 1024,
// Allow sidebar to be collapsed on desktop
'collapsible' => true,
];<?php
namespace App\Sidebar;
use Shopper\Sidebar\Contracts\Builder\Menu;
use Shopper\Sidebar\Contracts\Sidebar;
class AdminSidebar implements Sidebar
{
public function __construct(private Menu $menu) {}
public function build(): void
{
$this->menu->group('Main', function ($group) {
$group->item('Dashboard')
->url('/dashboard')
->icon('heroicon-o-home')
->weight(1);
$group->item('Users')
->url('/users')
->icon('heroicon-o-users')
->weight(2)
->items(function ($items) {
$items->item('All Users')->url('/users');
$items->item('Create User')->url('/users/create');
});
});
$this->menu->group('Settings', function ($group) {
$group->item('General')
->url('/settings')
->icon('heroicon-o-cog');
});
}
public function getMenu(): Menu
{
return $this->menu;
}
}Create an event listener or use the SidebarBuilder event:
<?php
namespace App\Providers;
use App\Sidebar\AdminSidebar;
use Illuminate\Support\ServiceProvider;
use Shopper\Sidebar\SidebarManager;
class SidebarServiceProvider extends ServiceProvider
{
public function boot(SidebarManager $manager): void
{
$manager->register(AdminSidebar::class);
}
}The sidebar package provides two ways to render your sidebar:
Use the built-in Livewire component for a ready-to-use sidebar with Alpine.js state management:
@livewire('sidebar', [
'sidebarClass' => \App\Sidebar\AdminSidebar::class,
'class' => 'your-sidebar-classes',
'collapsible' => true,
])The Livewire component includes:
- Alpine.js store integration (
$store.sidebar) - Collapse/expand functionality
- Responsive behavior (mobile/desktop)
- LocalStorage persistence
For complete control over the sidebar layout, use the SidebarRenderer directly in your Blade views:
{{-- layouts/sidebar.blade.php --}}
<aside
class="sidebar"
x-bind:class="{ 'sidebar-collapsed': $store.sidebar.isCollapsed }"
>
{{-- Custom header with branding --}}
<div class="sidebar-header">
<img src="/logo.png" alt="Logo" />
<span x-show="!$store.sidebar.isCollapsed">My App</span>
</div>
{{-- Sidebar menu (rendered by the package) --}}
<nav class="sidebar-nav">
{!! $sidebar !!}
</nav>
{{-- Custom footer --}}
<div class="sidebar-footer">
<a href="/settings">Settings</a>
</div>
</aside>Both options use the same underlying SidebarRenderer, which renders:
item.blade.php- Individual menu itemsgroup.blade.php- Menu groupsbadge.blade.php- Item badgesappend.blade.php- Appended content
The sidebar uses an Alpine.js store for state management. Initialize it in your JavaScript:
import Alpine from 'alpinejs'
import sidebarStore from '@shopper/sidebar/stores/sidebar'
Alpine.store('sidebar', sidebarStore())
Alpine.store('sidebar').init()// State
$store.sidebar.isOpen // Mobile: sidebar visibility
$store.sidebar.isCollapsed // Desktop: collapsed state
$store.sidebar.collapsible // Whether collapse is enabled
// Methods
$store.sidebar.toggle() // Smart toggle (mobile: open/close, desktop: collapse/expand)
$store.sidebar.open() // Open sidebar (mobile)
$store.sidebar.close() // Close sidebar (mobile)
$store.sidebar.collapse() // Collapse sidebar (desktop)
$store.sidebar.expand() // Expand sidebar (desktop)
$store.sidebar.toggleCollapse()
// Group management
$store.sidebar.toggleGroup(label)
$store.sidebar.isGroupCollapsed(label)The sidebar dimensions are available as CSS variables:
:root {
--sidebar-width: 16.5rem;
--sidebar-collapsed-width: 4.5rem;
}Inject them in your layout using the helper functions:
<style>
:root {
--sidebar-width: {{ \Shopper\Sidebar\sidebar_width() }};
--sidebar-collapsed-width: {{ \Shopper\Sidebar\sidebar_collapsed_width() }};
}
</style>
<body
data-sidebar-breakpoint="{{ \Shopper\Sidebar\sidebar_breakpoint() }}"
data-sidebar-collapsible="{{ \Shopper\Sidebar\sidebar_is_collapsible() ? 'true' : 'false' }}"
>The package provides namespaced helper functions:
\Shopper\Sidebar\sidebar_config($key, $default) // Get config value
\Shopper\Sidebar\sidebar_width() // Get sidebar width
\Shopper\Sidebar\sidebar_collapsed_width() // Get collapsed width
\Shopper\Sidebar\sidebar_breakpoint() // Get responsive breakpoint
\Shopper\Sidebar\sidebar_is_collapsible() // Check if collapsible$group->item('Label')
->url('/path') // URL
->route('route.name') // Or use route name
->icon('heroicon-o-home') // Icon (Blade UI Kit format)
->weight(1) // Sort order
->badge('New') // Add badge
->newTab() // Open in new tab
->authorize(fn() => auth()->check()) // Authorization callback
->items(function ($items) { // Sub-items
$items->item('Sub Item')->url('/sub');
});This package is licensed under MIT. You are free to use it in personal and commercial projects.