Sidebar

A composable, collapsible sidebar for application layouts.

Sidebar
Overview

Good morning, Pedro

Here is a summary of your workspace.

Total revenue

$45,231.89

+20.1% from last month

Subscriptions

+2,350

+180.1% from last month

Sales

+12,234

+19% from last month

Active now

+573

+201 since last hour

Component Code
<april:sidebar-layout class="w-full min-w-0 flex-nowrap">
<april:sidebar variant="floating" collapsible="icon">
<slot:header>
<april:dropdown-menu x-teleport="body" class="w-full">
<slot:trigger>
<april:button variant="ghost" size="none"
class="h-10 w-full justify-between px-2 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0">
<span class="flex min-w-0 items-center gap-2">
<span
class="flex size-7 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground">
<x-lucide-layers-3 class="size-4" />
</span>
<span class="truncate font-semibold group-data-[collapsible=icon]:hidden">Acme Inc.</span>
</span>
<x-lucide-chevrons-up-down class="size-4 shrink-0 group-data-[collapsible=icon]:hidden" />
</april:button>
</slot:trigger>
<slot:content>
<april:dropdown-menu-label>Switch workspace</april:dropdown-menu-label>
<april:dropdown-menu-separator />
<april:dropdown-menu-item>Acme Inc.
<april:dropdown-menu-shortcut>⌘1</april:dropdown-menu-shortcut></april:dropdown-menu-item>
<april:dropdown-menu-item>Design team
<april:dropdown-menu-shortcut>⌘2</april:dropdown-menu-shortcut></april:dropdown-menu-item>
<april:dropdown-menu-separator />
<april:dropdown-menu-item>Create workspace</april:dropdown-menu-item>
</slot:content>
</april:dropdown-menu>
<april:sidebar-input class="mt-2 group-data-[collapsible=icon]:hidden" placeholder="Search..." />
</slot:header>
<slot:content>
<april:sidebar-group>
<april:sidebar-group-label>Workspace</april:sidebar-group-label>
<april:sidebar-group-content>
<april:sidebar-menu>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#" :active="true">
<x-lucide-layout-dashboard />
<span>Overview</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-inbox />
<span>Inbox</span>
<april:sidebar-menu-badge>12</april:sidebar-menu-badge>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-calendar />
<span>Calendar</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-chart-line />
<span>Analytics</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-folder-kanban />
<span>Projects</span>
<april:sidebar-menu-badge>8</april:sidebar-menu-badge>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-users />
<span>Customers</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
</april:sidebar-menu>
</april:sidebar-group-content>
</april:sidebar-group>
<april:sidebar-group>
<april:sidebar-group-label>Manage</april:sidebar-group-label>
<april:sidebar-group-content>
<april:sidebar-menu>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-users />
<span>Team</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-settings />
<span>Settings</span>
</april:sidebar-menu-button-link>
<april:sidebar-menu-action :show-on-hover="true"
aria-label="Settings actions">•••</april:sidebar-menu-action>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-circle-help />
<span>Help center</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-list />
<span>Activity log</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">
<x-lucide-sliders-horizontal />
<span>Preferences</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
</april:sidebar-menu>
</april:sidebar-group-content>
</april:sidebar-group>
</slot:content>
<slot:footer>
<april:dropdown-menu x-teleport="body" class="w-full">
<slot:trigger>
<april:button variant="ghost" size="none"
class="h-10 w-full justify-start gap-2 px-2 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:justify-center">
<april:avatar size="sm">
<slot:fallback>PD</slot:fallback>
</april:avatar>
<span class="min-w-0 text-left group-data-[collapsible=icon]:hidden"><span
class="block truncate text-sm font-medium">Pedro Duarte</span><span
class="block truncate text-xs text-muted-foreground">pedro@example.com</span></span>
</april:button>
</slot:trigger>
<slot:content>
<april:dropdown-menu-label>My account</april:dropdown-menu-label>
<april:dropdown-menu-separator />
<april:dropdown-menu-item>Profile</april:dropdown-menu-item>
<april:dropdown-menu-item>Billing</april:dropdown-menu-item>
<april:dropdown-menu-item>Sign out</april:dropdown-menu-item>
</slot:content>
</april:dropdown-menu>
</slot:footer>
<april:sidebar-rail />
</april:sidebar>
<april:sidebar-inset>
<header class="flex h-14 items-center justify-between border-b px-4">
<div class="flex items-center gap-2">
<april:sidebar-trigger />
<span class="text-sm text-muted-foreground">Overview</span>
</div>
<april:button size="sm"><x-lucide-plus class="mr-2 size-4" />New project</april:button>
</header>
<main class="space-y-6 p-4 lg:p-6">
<div>
<h2 class="text-2xl font-semibold tracking-tight">Good morning, Pedro</h2>
<p class="text-sm text-muted-foreground">Here is a summary of your workspace.</p>
</div>
<div class="grid gap-4 sm:grid-cols-2">
<april:card>
<slot:title class="text-sm font-medium">Total revenue</slot:title>
<slot:content>
<div class="text-2xl font-bold">$45,231.89</div>
<p class="text-xs text-muted-foreground">+20.1% from last month</p>
</slot:content>
</april:card>
<april:card>
<slot:title class="text-sm font-medium">Subscriptions</slot:title>
<slot:content>
<div class="text-2xl font-bold">+2,350</div>
<p class="text-xs text-muted-foreground">+180.1% from last month</p>
</slot:content>
</april:card>
<april:card>
<slot:title class="text-sm font-medium">Sales</slot:title>
<slot:content>
<div class="text-2xl font-bold">+12,234</div>
<p class="text-xs text-muted-foreground">+19% from last month</p>
</slot:content>
</april:card>
<april:card>
<slot:title class="text-sm font-medium">Active now</slot:title>
<slot:content>
<div class="text-2xl font-bold">+573</div>
<p class="text-xs text-muted-foreground">+201 since last hour</p>
</slot:content>
</april:card>
</div>
</main>
</april:sidebar-inset>
</april:sidebar-layout>

The sidebar is not one component. It is a set of parts you put together, so you can build the layout your app needs instead of the one I guessed you needed.

Anatomy

Wrap everything in sidebar-layout. It holds the open state, so the sidebar, the trigger and the page content all agree on whether the sidebar is open.

layout.blade.php
<april:sidebar-layout>
<april:sidebar>
<slot:header>...</slot:header>
<slot:content>
<april:sidebar-group>
<april:sidebar-group-label>Platform</april:sidebar-group-label>
<april:sidebar-group-content>
<april:sidebar-menu>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="/inbox">Inbox</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
</april:sidebar-menu>
</april:sidebar-group-content>
</april:sidebar-group>
</slot:content>
<slot:footer>...</slot:footer>
<april:sidebar-rail />
</april:sidebar>
 
<april:sidebar-inset>
<april:sidebar-trigger />
{{ $slot }}
</april:sidebar-inset>
</april:sidebar-layout>

Menus

A menu is a list. Use sidebar-menu-button-link for a link and sidebar-menu-button for a button. Mark the current page with :active="true".

Add a badge for a count, an action for a row menu, and a submenu for children. The skeleton is there for while your data loads.

Component Code
<april:sidebar collapsible="none" class="w-64 border rounded-lg">
<slot:content>
<april:sidebar-group>
<april:sidebar-group-label>Projects</april:sidebar-group-label>
<april:sidebar-group-action>+</april:sidebar-group-action>
<april:sidebar-group-content>
<april:sidebar-menu>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#" :active="true">Design system</april:sidebar-menu-button-link>
<april:sidebar-menu-badge>24</april:sidebar-menu-badge>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">Marketing site</april:sidebar-menu-button-link>
<april:sidebar-menu-action :show-on-hover="true">...</april:sidebar-menu-action>
<april:sidebar-menu-sub>
<april:sidebar-menu-sub-item>
<april:sidebar-menu-sub-button href="#">Landing page</april:sidebar-menu-sub-button>
</april:sidebar-menu-sub-item>
<april:sidebar-menu-sub-item>
<april:sidebar-menu-sub-button href="#" :active="true">Pricing</april:sidebar-menu-sub-button>
</april:sidebar-menu-sub-item>
</april:sidebar-menu-sub>
</april:sidebar-menu-item>
<april:sidebar-separator />
<april:sidebar-menu-item>
<april:sidebar-menu-skeleton :show-icon="true" />
</april:sidebar-menu-item>
</april:sidebar-menu>
</april:sidebar-group-content>
</april:sidebar-group>
</slot:content>
</april:sidebar>

While the sidebar shows icons only, a menu button has no room for its label. Pass tooltip to give it one. The tooltip stays silent while the sidebar is open, because the button already says the same words.

<april:sidebar-menu-button tooltip="Boarding">
<x-lucide-bed-double />
<span>Boarding</span>
</april:sidebar-menu-button>

A menu button opens the sidebar when you click it. This is safe inside another component, because the button reaches the sidebar by name: a submenu trigger opens the sidebar and its own children on the same click. Pass :expand-sidebar="false" when a button must leave the sidebar alone:

<april:collapsible>
<slot:trigger>
<april:sidebar-menu-button tooltip="Boarding">
<x-lucide-bed-double />
<span>Boarding</span>
</april:sidebar-menu-button>
</slot:trigger>
<slot:content>...</slot:content>
</april:collapsible>

Collapsible

The collapsible attribute decides what happens when the sidebar closes. It takes three values.

Offcanvas

The default. The sidebar slides off the screen.

Sidebar
Acme Inc
Platform

Collapse the sidebar to see it shrink to icon width. Point at an icon to read its label.

Component Code
<april:sidebar-layout class="min-h-[620px] border rounded-lg overflow-hidden">
<april:sidebar collapsible="icon">
<slot:header>
<span class="font-semibold px-2">Acme Inc</span>
</slot:header>
<slot:content>
<april:sidebar-group>
<april:sidebar-group-label>Platform</april:sidebar-group-label>
<april:sidebar-group-content>
<april:sidebar-menu>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#" :active="true" tooltip="Inbox">
<x-lucide-inbox />
<span>Inbox</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#" tooltip="Calendar">
<x-lucide-calendar />
<span>Calendar</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
</april:sidebar-menu>
</april:sidebar-group-content>
</april:sidebar-group>
</slot:content>
<april:sidebar-rail />
</april:sidebar>
<april:sidebar-inset class="p-4">
<april:sidebar-trigger />
<p class="mt-4 text-sm text-muted-foreground">Collapse the sidebar to see it shrink to icon width. Point at an icon to read its label.</p>
</april:sidebar-inset>
</april:sidebar-layout>


Icon

The sidebar shrinks to icon width and keeps the icons visible. Labels, badges, actions and submenus hide themselves.

A long menu still scrolls when the sidebar is collapsed. The scrollbar is hidden so it does not take room from the icons.

<april:sidebar collapsible="icon">...</april:sidebar>

None

The sidebar never collapses. Use it when you want a plain, always open panel.

<april:sidebar collapsible="none">...</april:sidebar>

Side

Use the side attribute to put the sidebar on the right. The border, the rail and the mobile panel all follow.

The sidebar sits on the right.

Sidebar
Acme Inc
Component Code
<april:sidebar-layout class="min-h-[620px] border rounded-lg overflow-hidden">
<april:sidebar-inset class="p-4">
<april:sidebar-trigger />
<p class="mt-4 text-sm text-muted-foreground">The sidebar sits on the right.</p>
</april:sidebar-inset>
<april:sidebar side="right">
<slot:header>
<span class="font-semibold px-2">Acme Inc</span>
</slot:header>
<slot:content>
<april:sidebar-group>
<april:sidebar-group-content>
<april:sidebar-menu>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">Inbox</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
</april:sidebar-menu>
</april:sidebar-group-content>
</april:sidebar-group>
</slot:content>
<april:sidebar-rail />
</april:sidebar>
</april:sidebar-layout>


Variants

The variant attribute takes sidebar, floating or inset. floating rounds the panel and gives it a border. inset pulls the page content in and rounds it instead.

Sidebar
Acme Inc

The floating variant rounds the panel and adds a border.

Component Code
<april:sidebar-layout class="min-h-[620px] border rounded-lg overflow-hidden">
<april:sidebar variant="floating" collapsible="icon">
<slot:header>
<span class="font-semibold px-2">Acme Inc</span>
</slot:header>
<slot:content>
<april:sidebar-group>
<april:sidebar-group-content>
<april:sidebar-menu>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#" :active="true">
<x-lucide-inbox />
<span>Inbox</span>
</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
</april:sidebar-menu>
</april:sidebar-group-content>
</april:sidebar-group>
</slot:content>
</april:sidebar>
<april:sidebar-inset class="p-4">
<april:sidebar-trigger />
<p class="mt-4 text-sm text-muted-foreground">The floating variant rounds the panel and adds a border.</p>
</april:sidebar-inset>
</april:sidebar-layout>


Search

sidebar-input is the input component with the sidebar sizing already applied.

Component Code
<april:sidebar collapsible="none" class="w-64 border rounded-lg">
<slot:header>
<april:sidebar-input name="search" placeholder="Search the docs..." />
</slot:header>
<slot:content>
<april:sidebar-group>
<april:sidebar-group-content>
<april:sidebar-menu>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">Installation</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
<april:sidebar-menu-item>
<april:sidebar-menu-button-link href="#">Theming</april:sidebar-menu-button-link>
</april:sidebar-menu-item>
</april:sidebar-menu>
</april:sidebar-group-content>
</april:sidebar-group>
</slot:content>
</april:sidebar>


Opening and closing

Three things toggle the sidebar:

  • sidebar-trigger, a button you can put anywhere inside the layout
  • sidebar-rail, the thin strip along the sidebar edge
  • Ctrl + B, or Cmd + B on a Mac

A menu button opens the sidebar as well, unless you pass :expand-sidebar="false".

On a screen narrower than 768px the sidebar becomes a panel that slides in over the page. The same three things still open it.

Start the sidebar closed with :default-open="false".

<april:sidebar-layout :default-open="false">...</april:sidebar-layout>

Reading the sidebar state

The layout keeps the whole sidebar under one sidebar name. Reach for it with that receiver, the way you would call a hook:

<button type="button" x-on:click="sidebar.toggle()">Toggle</button>
<span x-show="sidebar.state === 'collapsed'">Icons only</span>
Name What it holds
sidebar.open The desktop state. sidebar-layout binds it with x-modelable.
sidebar.openMobile The mobile panel. It is never stored.
sidebar.isMobile True below 768px.
sidebar.state expanded or collapsed.
sidebar.isOpen() The state of whichever panel the screen width uses.
sidebar.toggle() Flip the state.
sidebar.show() Open it.
sidebar.close() Close it.

The name is the point. Alpine reads a bare name against the scopes of the element that holds the expression, so open inside a collapsible means the collapsible, not the sidebar. The sidebar wraps your whole page, so a bare name would sooner or later meet a component that owns the same one.


Persisting state

To keep the sidebar open or collapsed across page loads, pass x-persist with a unique key. The component applies the x-persist directive to both the desktop and mobile sidebars for you.

<april:sidebar x-persist="app-sidebar">...</april:sidebar>

Width

The sidebar reads three CSS variables. Set them on sidebar-layout to change the width for one sidebar:

<april:sidebar-layout width="20rem" width-icon="4rem">...</april:sidebar-layout>

Or set them in your CSS to change every sidebar:

app.css
:root {
--sidebar-width: 16rem;
--sidebar-width-icon: 3rem;
--sidebar-width-mobile: 18rem;
}

Styling from your own CSS

Every part carries a data-slot attribute, and the sidebar carries its state. You can style any part without touching the markup:

app.css
/* The menu button of the current page */
[data-slot="sidebar-menu-button"][data-active="true"] {
font-weight: 600;
}
 
/* Only while the sidebar is collapsed */
[data-slot="sidebar"][data-state="collapsed"] [data-slot="sidebar-header"] {
justify-content: center;
}

See Data attributes for the full list.

Publishing Views

Use the following commands to publish these views:

php artisan april:publish sidebar-layout
php artisan april:publish sidebar
php artisan april:publish sidebar-trigger
php artisan april:publish sidebar-rail
php artisan april:publish sidebar-inset
php artisan april:publish sidebar-input
php artisan april:publish sidebar-separator
php artisan april:publish sidebar-group
php artisan april:publish sidebar-group-label
php artisan april:publish sidebar-group-action
php artisan april:publish sidebar-group-content
php artisan april:publish sidebar-menu
php artisan april:publish sidebar-menu-item
php artisan april:publish sidebar-menu-button
php artisan april:publish sidebar-menu-button-link
php artisan april:publish sidebar-menu-action
php artisan april:publish sidebar-menu-badge
php artisan april:publish sidebar-menu-skeleton
php artisan april:publish sidebar-menu-sub
php artisan april:publish sidebar-menu-sub-item
php artisan april:publish sidebar-menu-sub-button
April UI keeps package views in `vendor/` by default. The command uses Laravel's vendor publishing system and copies overrides to `resources/views/vendor/april/components`. To publish all components, run:
php artisan april:publish --all
Review local overrides after an upgrade with `php artisan april:update --diff`.