Sidebar
A collapsible application sidebar with persistent state, accessible navigation, and an optional draggable resize handle that remembers its width.
Preview
Dashboard
Controls
Device view renders in an isolated frame so real responsive breakpoints apply. Switch to Full for overflowing popovers.
Installation
php artisan ui:add sidebar
Behavior layer (Alpine)
This component installs a self-registering Alpine module to
resources/js/ui/sidebar.js. Import the behavior layer once
from your bundle so it loads (it hooks alpine:init):
import './sidebar.js';
Registry contract
Install confidence
php artisan ui:add sidebar
writes only the generated targets below. The CLI validates each file hash before writing and
prompts before replacing local changes unless --force is used.
- Version
- 1.2.0
- License
- open
- Stability
- stable
- Contract
- v2
- Foundation
- ≥ 1.0.0
| Type | Generated target |
|---|---|
| blade | resources/views/components/ui/sidebar.blade.php |
| blade | resources/views/components/ui/sidebar/trigger.blade.php |
| blade | resources/views/components/ui/sidebar/header.blade.php |
| blade | resources/views/components/ui/sidebar/content.blade.php |
| blade | resources/views/components/ui/sidebar/footer.blade.php |
| blade | resources/views/components/ui/sidebar/group.blade.php |
| blade | resources/views/components/ui/sidebar/group-label.blade.php |
| blade | resources/views/components/ui/sidebar/menu.blade.php |
| blade | resources/views/components/ui/sidebar/menu-item.blade.php |
| blade | resources/views/components/ui/sidebar/menu-button.blade.php |
| blade | resources/views/components/ui/sidebar/separator.blade.php |
| blade | resources/views/components/ui/sidebar/inset.blade.php |
| js | resources/js/ui/sidebar.js |
Registry dependencies
Package dependencies
composer: jml/brok:^1.0
npm: alpinejs
Guidance
Layout and content structureStructure content hierarchy and responsive relationships.
Use when
- Use to structure hierarchy, spacing, and responsiveness so content is easier to scan and navigate. Do not let layout primitives substitute for semantics, headings, or interaction rules users still need.
Avoid when
- Avoid using sidebar as a default simply because it is available in the library. Choose it only when its interaction cost, visibility, and behavior match the job to be done.
- Task length
- short
- Interaction cost
- low
- Interruptiveness
- none
- Input precision
- none
- Mobile / touch risk
- low
- Accessibility risk
- medium
- Needs background context
- No
- Comparison needed
- No
Preferred alternatives
- Semantic HTML with standard flow
Anti-patterns
- Using visual layout as a substitute for semantic structure
API
manifest knowledge + registry-derived coverageProps
| Prop | Type / values | Default | Description |
|---|---|---|---|
| resizable | bool | false | Declared by @props in the registry Blade source. |
| minWidth | int | 200 | Declared by @props in the registry Blade source. |
| maxWidth | int | 480 | Declared by @props in the registry Blade source. |
| defaultWidth | int | 256 | Declared by @props in the registry Blade source. |
| storageKey | mixed|null | null | Declared by @props in the registry Blade source. |
| label | string | Primary | Declared by @props in the registry Blade source. |
| href | mixed|null | null | Declared by @props in the registry Blade source. |
| active | bool | false | Declared by @props in the registry Blade source. |
Slots
default— Compose sidebar trigger, header, content, groups, menu items, separators, footer, and inset.x-ui.sidebar.trigger— Installed subcomponent from the registry item.x-ui.sidebar.header— Installed subcomponent from the registry item.x-ui.sidebar.content— Installed subcomponent from the registry item.x-ui.sidebar.footer— Installed subcomponent from the registry item.x-ui.sidebar.group— Installed subcomponent from the registry item.x-ui.sidebar.group-label— Installed subcomponent from the registry item.x-ui.sidebar.menu— Installed subcomponent from the registry item.x-ui.sidebar.menu-item— Installed subcomponent from the registry item.x-ui.sidebar.menu-button— Installed subcomponent from the registry item.x-ui.sidebar.separator— Installed subcomponent from the registry item.x-ui.sidebar.inset— Installed subcomponent from the registry item.
Data slots
sidebar
sidebar-content
sidebar-footer
sidebar-group
sidebar-group-label
sidebar-header
sidebar-inset
sidebar-menu
sidebar-menu-button
sidebar-menu-item
sidebar-resize-handle
sidebar-separator
sidebar-trigger
Capabilities
Behavior
- Uses server-rendered Blade and semantic HTML.
- Persists expanded/collapsed state and exposes data-state for responsive layout styling.
- Trigger updates aria-expanded and the menu remains navigable when collapsed.
- Installs a JavaScript behavior module when the registry item includes resources/js/ui files.
- Declares registry capability flags: a11y, interactive, reducedMotion, responsive, rtl, darkMode, localized.
Usage
<div class="flex h-80 overflow-hidden rounded-lg border border-border" data-surface="admin">
<x-ui.sidebar>
<x-ui.sidebar.header>
<x-ui.sidebar.trigger />
<span x-show="expanded" class="text-sm font-semibold text-foreground">{{ __('Acme Inc') }}</span>
</x-ui.sidebar.header>
<x-ui.sidebar.content>
<x-ui.sidebar.group>
<x-ui.sidebar.group-label>{{ __('Platform') }}</x-ui.sidebar.group-label>
<x-ui.sidebar.menu>
<x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-button :active="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg>
<span x-show="expanded">{{ __('Dashboard') }}</span>
</x-ui.sidebar.menu-button>
</x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-button>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
<span x-show="expanded">{{ __('Team') }}</span>
</x-ui.sidebar.menu-button>
</x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-button>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v16a2 2 0 0 0 2 2h16"/><path d="M18 17V9"/><path d="M13 17V5"/><path d="M8 17v-3"/></svg>
<span x-show="expanded">{{ __('Analytics') }}</span>
</x-ui.sidebar.menu-button>
</x-ui.sidebar.menu-item>
</x-ui.sidebar.menu>
</x-ui.sidebar.group>
</x-ui.sidebar.content>
<x-ui.sidebar.footer>
<x-ui.sidebar.menu-button>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
<span x-show="expanded">{{ __('Settings') }}</span>
</x-ui.sidebar.menu-button>
</x-ui.sidebar.footer>
</x-ui.sidebar>
<x-ui.sidebar.inset class="p-4">
<h2 class="text-lg font-semibold text-foreground">{{ __('Dashboard') }}</h2>
</x-ui.sidebar.inset>
</div>
Variants
Dashboard
Drag the edge of the sidebar to resize it, or focus the handle and use the arrow keys. The width is remembered.
<div class="flex h-80 overflow-hidden rounded-lg border border-border" data-surface="admin">
<x-ui.sidebar resizable :min-width="180" :max-width="360" :default-width="256" storage-key="demo:sidebar-width">
<x-ui.sidebar.header>
<x-ui.sidebar.trigger />
<span x-show="expanded" class="text-sm font-semibold text-foreground">{{ __('Acme Inc') }}</span>
</x-ui.sidebar.header>
<x-ui.sidebar.content>
<x-ui.sidebar.group>
<x-ui.sidebar.group-label>{{ __('Platform') }}</x-ui.sidebar.group-label>
<x-ui.sidebar.menu>
<x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-button :active="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg>
<span x-show="expanded">{{ __('Dashboard') }}</span>
</x-ui.sidebar.menu-button>
</x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-button>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
<span x-show="expanded">{{ __('Team') }}</span>
</x-ui.sidebar.menu-button>
</x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-item>
<x-ui.sidebar.menu-button>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v16a2 2 0 0 0 2 2h16"/><path d="M18 17V9"/><path d="M13 17V5"/><path d="M8 17v-3"/></svg>
<span x-show="expanded">{{ __('Analytics') }}</span>
</x-ui.sidebar.menu-button>
</x-ui.sidebar.menu-item>
</x-ui.sidebar.menu>
</x-ui.sidebar.group>
</x-ui.sidebar.content>
<x-ui.sidebar.footer>
<x-ui.sidebar.menu-button>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
<span x-show="expanded">{{ __('Settings') }}</span>
</x-ui.sidebar.menu-button>
</x-ui.sidebar.footer>
</x-ui.sidebar>
<x-ui.sidebar.inset class="p-4">
<h2 class="text-lg font-semibold text-foreground">{{ __('Dashboard') }}</h2>
<p class="mt-1 text-sm text-muted-foreground">{{ __('Drag the edge of the sidebar to resize it, or focus the handle and use the arrow keys. The width is remembered.') }}</p>
</x-ui.sidebar.inset>
</div>
Accessibility & localization
data-slot
Keyboard focus-visible
Localized labels
- Accessibility target: wcag-2.2-aa-target.
- Uses semantic HTML and a stable
data-slotattribute for styling/scripting hooks. - Focus-visible rings use the
ringtoken so keyboard focus is always visible. - Disabled and invalid states are conveyed to assistive tech, not by color alone.
- Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces above.
- Labels go through
__()and the component uses logical properties (ms-*,text-start,start-*) so it mirrors correctly — flip the preview to RTL to confirm.
Source
The exact, editable file ui:add writes
into your app — copy it or own the whole thing. Previews
render this same code; there are no preview-only components.
Changelog
Current version: v1.2.0. See the site changelog for the full release history.