Sidebar
A versatile sidebar component that can be used for navigation, content display, or actions. It is highly composable and supports various collapsible behaviors, variants, and layouts.
Under the Hood
The entire sidebar system is managed by the RzSidebarProvider, which hosts an Alpine.js component (x-data="rzSidebar") to control its state and interactivity, including collapsible behavior and keyboard shortcuts.
Default Sidebar
A basic sidebar layout with a header for actions like version switching and search, and a content area with grouped navigation links. The main content area is inset next to the sidebar.
<RzSidebarProvider>
<Sidebar>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<RzDropdownMenu>
<DropdownMenuTrigger AsChild>
<SidebarMenuButton Size="Size.Large">
<div class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
<Blazicon Svg="MdiIcon.BookOpenVariant" class="size-4" />
</div>
<div class="flex flex-col gap-0.5 leading-none">
<span class="font-medium">Documentation</span>
<span>v1.0.1</span>
</div>
<Blazicon Svg="FluentUiIcon.ChevronUpDown" class="ml-auto" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>v1.0.1</DropdownMenuItem>
<DropdownMenuItem>v1.1.0-alpha</DropdownMenuItem>
<DropdownMenuItem>v2.0.0-beta1</DropdownMenuItem>
</DropdownMenuContent>
</RzDropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
<div class="relative">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<Blazicon Svg="MdiIcon.Magnify" class="size-4 text-muted-foreground" />
</div>
<input type="search"
name="search"
id="sidebar-search-1"
placeholder="Search the docs..."
class="appearance-none file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive pl-8" />
</div>
</SidebarHeader>
<SidebarContent>
@foreach (var item in _navMain)
{
<SidebarGroup>
<SidebarGroupLabel>@item.Title</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
@foreach (var subItem in item.Items)
{
<SidebarMenuItem>
<SidebarMenuButton AsChild="true" IsActive="subItem.IsActive">
<a href="#">@subItem.Title</a>
</SidebarMenuButton>
</SidebarMenuItem>
}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
}
</SidebarContent>
</Sidebar>
<SidebarInset>
<header class="flex h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger class="-ml-1" />
<RzSeparator Orientation="Orientation.Vertical" class="mr-2 h-4" />
<RzBreadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink Href="#">Building Your Application</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</RzBreadcrumb>
</header>
<div class="flex flex-1 flex-col gap-4 p-4">
<div class="bg-muted/50 aspect-video rounded-xl" />
</div>
</SidebarInset>
</RzSidebarProvider>
@code {
public class NavItem {
public string Title { get; set; } = "";
public bool IsActive { get; set; }
}
public class NavGroup {
public string Title { get; set; } = "";
public List<NavItem> Items { get; set; } = new();
}
private List<NavGroup> _navMain = new()
{
new() {
Title = "Getting Started",
Items = new() { new() { Title = "Installation" }, new() { Title = "Project Structure" } }
},
new() {
Title = "Building Your Application",
Items = new() { new() { Title = "Routing" }, new() { Title = "Data Fetching", IsActive = true }, new() { Title = "Rendering" } }
}
};
}Collapsible Groups
This example demonstrates collapsible sidebar groups using the RzCollapsible component, allowing users to expand and collapse navigation sections.
<RzSidebarProvider>
<Sidebar>
<SidebarContent class="gap-0">
@foreach (var item in _navMain)
{
<RzCollapsible class="group/collapsible" DefaultOpen="@item.Items.Any(i => i.IsActive)">
<SidebarGroup>
<SidebarGroupLabel AsChild class="group/label text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground text-sm">
<CollapsibleTrigger>
@item.Title
<Blazicon Svg="MdiIcon.ChevronRight" class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90" />
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarGroupContent>
<SidebarMenu>
@foreach (var subItem in item.Items)
{
<SidebarMenuItem>
<SidebarMenuButton AsChild IsActive="subItem.IsActive">
<a href="#">@subItem.Title</a>
</SidebarMenuButton>
</SidebarMenuItem>
}
</SidebarMenu>
</SidebarGroupContent>
</CollapsibleContent>
</SidebarGroup>
</RzCollapsible>
}
</SidebarContent>
</Sidebar>
<SidebarInset>
<header class="bg-background sticky top-0 flex h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger class="-ml-1" />
</header>
<div class="flex flex-1 flex-col gap-4 p-4">
<div class="bg-muted/50 aspect-video rounded-xl" />
</div>
</SidebarInset>
</RzSidebarProvider>Nested Sub-menus
This example showcases a sidebar with nested sub-menus for a hierarchical navigation structure.
<RzSidebarProvider>
<Sidebar>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton Size="Size.Large" AsChild>
<a href="#">
<div class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
<Blazicon Svg="MdiIcon.BookOpenVariant" class="size-4" />
</div>
<div class="flex flex-col gap-0.5 leading-none">
<span class="font-medium">Documentation</span>
<span>v1.0.0</span>
</div>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarMenu>
@foreach (var item in _navMain)
{
<SidebarMenuItem>
<SidebarMenuButton AsChild>
<a href="#" class="font-medium">@item.Title</a>
</SidebarMenuButton>
@if (item.Items.Any())
{
<SidebarMenuSub>
@foreach (var subItem in item.Items)
{
<SidebarMenuItem>
<SidebarMenuButton AsChild="true" IsActive="subItem.IsActive">
<a href="#">@subItem.Title</a>
</SidebarMenuButton>
</SidebarMenuItem>
}
</SidebarMenuSub>
}
</SidebarMenuItem>
}
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
</Sidebar>
<SidebarInset>
<header class="flex h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger class="-ml-1" />
</header>
<div class="flex flex-1 flex-col gap-4 p-4">
<div class="bg-muted/50 aspect-video rounded-xl" />
</div>
</SidebarInset>
</RzSidebarProvider>Floating Sidebar
A floating sidebar with a margin from the viewport edges, achieved by setting Variant="SidebarVariant.Floating".
<RzSidebarProvider Width="19rem">
<Sidebar Variant="SidebarVariant.Floating">
<!-- Sidebar content from previous examples -->
</Sidebar>
<SidebarInset>
<!-- Main content -->
</SidebarInset>
</RzSidebarProvider>Collapsible with +/- Icons
This example uses RzCollapsible with custom icons to indicate the open/closed state of each navigation group.
<RzCollapsible class="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger AsChild>
<SidebarMenuButton>
Menu Title
<Blazicon Svg="MdiIcon.Plus" class="ml-auto group-data-[state=open]/collapsible:hidden" />
<Blazicon Svg="MdiIcon.Minus" class="ml-auto group-data-[state=closed]/collapsible:hidden" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<!-- Sub-menu items -->
</CollapsibleContent>
</SidebarMenuItem>
</RzCollapsible>Dropdown Navigation
This sidebar uses dropdown menus for each main navigation item, providing a compact way to access sub-links. The footer contains a newsletter sign-up form within a card.
<RzSidebarProvider>
<Sidebar>
<!-- Header -->
<SidebarContent>
<SidebarGroup>
<SidebarMenu>
@foreach (var item in _navMain)
{
<RzDropdownMenu Anchor="AnchorPoint.BottomEnd">
<DropdownMenuTrigger AsChild>
<SidebarMenuButton>
@item.Title
<Blazicon Svg="MdiIcon.DotsHorizontal" class="ml-auto" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent Side="right" Align="start">
@foreach (var subItem in item.Items)
{
<DropdownMenuItem>@subItem.Title</DropdownMenuItem>
}
</DropdownMenuContent>
</RzDropdownMenu>
}
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<div class="p-1">
<RzCard class="gap-2 py-4 shadow-none">
<CardHeader class="px-4">
<CardTitle class="text-sm">Subscribe to our newsletter</CardTitle>
<CardDescription>Opt-in to receive updates and news about the sidebar.</CardDescription>
</CardHeader>
<CardContent class="px-4">
<form>
<div class="grid gap-2.5">
<input type="email" placeholder="Email" class="..." />
<RzButton class="bg-sidebar-primary text-sidebar-primary-foreground w-full shadow-none" Size="Size.Small">
Subscribe
</RzButton>
</div>
</form>
</CardContent>
</RzCard>
</div>
</SidebarFooter>
</Sidebar>
<SidebarInset>
<!-- Main content -->
</SidebarInset>
</RzSidebarProvider>Icon Collapsible Sidebar
This sidebar collapses to an icon-only view, providing a compact navigation experience. Set Collapsible="SidebarCollapsible.Icon" on the RzSidebarProvider.
Inset Sidebar
An inset sidebar is surrounded by a margin and appears to be "inset" within the main content area. This is achieved by setting Variant="SidebarVariant.Inset" on the Sidebar component.
Complex Navigation
A more complex sidebar with multiple distinct navigation sections for main actions, favorites, workspaces, and secondary links.
File Explorer
A sidebar styled as a file explorer, using nested collapsible sections to represent directories and files.
Sidebar in a Modal
This example demonstrates embedding a non-collapsible sidebar within an RzDialog component to create a settings dialog.
Right-aligned Sidebar
A sidebar aligned to the right side of the viewport by setting Side="SidebarSide.Right" on the Sidebar component.
Accessibility
The sidebar follows a responsive accessibility contract. On desktop it behaves as a persistent layout/navigation region and does not trap focus. On mobile it is rendered through RzSheet as a modal offcanvas panel, so focus moves into the panel while it is open and returns to the invoking control when the sheet closes.
| Interaction | Behavior |
|---|---|
| Ctrl/Cmd + B | Toggles the desktop collapsed state or the mobile offcanvas state. Change the final key with KeyboardShortcut. |
| Escape | Closes the mobile offcanvas panel through the underlying sheet dismissable-layer behavior. Desktop mode is persistent and is not closed by Escape. |
| Outside click | Dismisses the mobile offcanvas panel through the sheet overlay. Desktop mode remains reachable and does not apply a focus trap. |
| Tab | Moves naturally through the page on desktop. In mobile modal mode, tab focus is contained by the sheet focus scope until the panel closes. |
SidebarTrigger renders a button with an accessible label, aria-controls pointing at the provider root, and a runtime-bound aria-expanded value that reflects the current desktop or mobile state. Active sidebar menu buttons set aria-current="page" when IsActive is true and no consumer-provided aria-current value is already present.
The runtime is SSR and CSP safe: state is initialized from data attributes, Alpine bindings own browser-only interactions, and no Blazor event callback or circuit is required. The rzSidebar runtime emits serializable rz:sidebar:state-change, rz:sidebar:breakpoint-change, rz:sidebar:mobile-open, and rz:sidebar:mobile-close events for observation.
Limitations
The sidebar does not infer the current route. Provide IsActive or your own aria-current value on menu buttons and links when representing the current page. Desktop collapse state is persisted with the configured cookie name only when persistence is enabled.
Component Parameters
The sidebar system is composed of multiple components that work together. Below are the parameters for each component.
RzSidebarProvider
| Property | Type | Default | Description |
|---|---|---|---|
ChildContent | RenderFragment | Required | The content of the provider, which should include a Sidebar. |
DefaultOpen | bool | true | The initial open state of the sidebar on desktop. |
Collapsible | SidebarCollapsible | OffCanvas | The collapsible behavior of the sidebar. |
KeyboardShortcut | string | b | The keyboard key for the open/close shortcut (Cmd/Ctrl + key). |
PersistenceCookieName | string? | sidebar_state | The name of the cookie to persist the sidebar's state. Null or empty disables persistence. |
Width | string | 16rem | The width of the sidebar on desktop. |
MobileWidth | string | 18rem | The width of the sidebar on mobile. |
IconWidth | string | 3rem | The width of the sidebar when collapsed to icon-only mode. |
Sidebar
| Property | Type | Default | Description |
|---|---|---|---|
ChildContent | RenderFragment | Required | The content of the sidebar. |
Side | SidebarSide | Left | The side of the screen where the sidebar appears. |
Variant | SidebarVariant | Sidebar | The visual variant of the sidebar. |
AriaLabel | string? | Localized "Sidebar navigation" | Accessible label for the sidebar. |
Alpine API
This page uses the rzSidebar Alpine x-data component.
| Method | Parameters | Description |
|---|---|---|
toggle | — | Toggles sidebar state based on viewport mode. |
setOpen | bool | Sets the desktop expanded/collapsed state and updates the persistence cookie when enabled. |
setOpenMobile | bool | Sets the mobile offcanvas open state. |
close | — | Closes the mobile sidebar when the runtime is in mobile mode. |
isMobileOpen | — | Returns whether the mobile sidebar is open. |
destroy | — | Removes global keydown and resize listeners when Alpine tears down the component. |
Events
| Event | Payload |
|---|---|
rz:sidebar:state-change | Emitted after desktop or mobile state changes with open, openMobile, isMobile, desktopState, mobileState, and collapsible. |
rz:sidebar:breakpoint-change | Emitted when the viewport crosses the configured mobile breakpoint and mobile state is reset closed. |
rz:sidebar:mobile-open / rz:sidebar:mobile-close | Emitted when the mobile offcanvas opens or closes. |