Skip to content
IzzyBlazor 0.1.0-alpha.17

Enter all results ↑ ↓ move Esc close

AppSidebar

A sidebar that becomes a drawer or panel on small screens.

A static page with IzzyBlazor's script. No circuit.

Usage

razor
<AppShell>
    <AppHeader>
        <SidebarToggle />
        <AppBrand Href="/" Icon="Lucide.Zap" Label="Acme" />
    </AppHeader>
    <AppSidebar>
        <NavMenu>
            <NavItem Href="/" Icon="Lucide.House">Home</NavItem>
            <NavItem Href="/projects" Icon="Lucide.Folder">Projects</NavItem>
        </NavMenu>
    </AppSidebar>
    <AppMain>@Body</AppMain>
</AppShell>

A sidebar is a part of an AppShell: a column of links beside the page on wide screens and, on phones, a drawer that the SidebarToggle in the header opens. Both work with JavaScript off.

A drawer on phones

Use Breakpoint for the width below which the sidebar becomes a drawer, 768px by default; the menu button shows only below it.

Preview width

Preview

Code

Drawer.razor
@inject NavigationManager Navigation

@* From 768px up the sidebar is a column beside the page; below it, a drawer behind the menu
   button, with SidebarHeader in its top row beside the close button. SidebarFooter stays at the
   bottom while the links between scroll. *@
<AppShell>
    <AppHeader>
        <SidebarToggle />
        <AppBrand Href="@To(null)" Icon="Lucide.Zap" Label="Acme" />
    </AppHeader>

    <AppSidebar>
        <SidebarHeader>
            <Row Gap="2" Align="Align.Center">
                <Avatar Name="Marketing" Size="AvatarSize.Sm" Square Decorative />
                <TextBlock As="span" Weight="FontWeight.Semibold">Marketing</TextBlock>
            </Row>
        </SidebarHeader>
        <NavMenu>
            <NavItem Href="@To(null)" Icon="Lucide.House">Home</NavItem>
            <NavItem Href='@To("campaigns")' Icon="Lucide.Megaphone" Badge="3">Campaigns</NavItem>
            <NavItem Href='@To("audiences")' Icon="Lucide.Users">Audiences</NavItem>
            <NavItem Href='@To("reports")' Icon="Lucide.ChartColumn">Reports</NavItem>
        </NavMenu>
        <SidebarFooter>
            <NavMenu aria-label="Account">
                <NavItem Href='@To("account")' Icon="Lucide.CircleUser">Ada Lovelace</NavItem>
            </NavMenu>
        </SidebarFooter>
    </AppSidebar>

    <AppMain>
        <Stack Gap="4" Padding="new() { Base = 4, Md = 8 }" MaxWidth="Length.Prose">
            <Heading Level="1" Size="TextSize.Xl2">Home</Heading>
            <TextBlock Color="Color.Muted">On a phone, the menu button at the top opens the sidebar as a drawer over the page. Its close button or a link closes it again, and with the script, Escape.</TextBlock>
            <Card Title="Spring launch" Description="Scheduled for March 3, three channels." />
        </Stack>
    </AppMain>
</AppShell>

@code {
    // Links within this example: ?view= in its own address, so they stay here.
    private string To(string? page) => Navigation.GetUriWithQueryParameter("view", page);
}

Static: At phone width the drawer opens without changing the address, and focus moves into it. Its close button, Escape or a tap beside it closes it and puts focus back on the menu button; following a link closes it too.

Use DrawerPosition="SidebarPosition.End" to open the drawer from the right, with the menu button at the right of the header, as content sites do; Position moves the whole sidebar there, and Width sets its width, 16rem by default.

razor
@* On the left on wide screens; on phones the drawer opens from the right,
   and the menu button sits at the right of the header. *@
<AppSidebar DrawerPosition="SidebarPosition.End" Breakpoint="Breakpoint.Lg">
    <NavMenu>...</NavMenu>
</AppSidebar>

Pinned at the top and bottom

Use SidebarHeader for what stays at the top, such as a workspace, and SidebarFooter for what stays at the bottom, such as the account, while the links between them scroll; write both directly in the sidebar, as the drawer example does.

razor
<AppSidebar>
    <SidebarHeader>
        <AppBrand Href="/" Icon="Lucide.Zap" Label="Acme" />
    </SidebarHeader>
    <SidebarBody Gap="4">
        <NavMenu>...</NavMenu>
        <NavMenu aria-label="Projects">...</NavMenu>
    </SidebarBody>
    <SidebarFooter>
        <NavMenu aria-label="Account">
            <NavItem Href="/account" Icon="Lucide.CircleUser">Ada Lovelace</NavItem>
        </NavMenu>
    </SidebarFooter>
</AppSidebar>

In the drawer the header shares the top row with the close button. With no SidebarHeader, the drawer's top row shows the shell's AppBrand. Use SidebarBody only for spacing, such as a Gap between two lists: links written straight in the sidebar are its body too.

A panel under the header

Use SmallScreen="SidebarSmallScreen.Panel" to open the sidebar under the header on phones, full width, with the header still in view and the menu button turned into its close button.

Preview width

Preview

Code

Panel.razor
@inject NavigationManager Navigation

@* A guide with site links in the header and chapters in the sidebar. On phones the sidebar opens
   as a panel under the header (SmallScreen.Panel) with the header's links at its top, so there is
   one menu, and the menu button turns into its close button. *@
<AppShell>
    <AppHeader>
        <SidebarToggle />
        <AppBrand Href="@To(null)" Icon="Lucide.Zap" Label="Acme" />
        <Box Grow />
        <SiteNav>
            <NavItem Href="@Site(null)">Guide</NavItem>
            <NavItem Href='@Site("blog")'>Blog</NavItem>
            <NavItem Href='@Site("videos")'>Videos</NavItem>
        </SiteNav>
    </AppHeader>

    <AppSidebar SmallScreen="SidebarSmallScreen.Panel" Label="Guide">
        <NavMenu>
            <NavLabel>Basics</NavLabel>
            <NavItem Href="@To(null)" Icon="Lucide.BookOpen">Writing your first rule</NavItem>
            <NavItem Href='@To("guide/outcomes")' Icon="Lucide.ListChecks">Outcomes</NavItem>
            <NavItem Href='@To("guide/dry-runs")' Icon="Lucide.Play">Dry runs</NavItem>
            <NavLabel>Going live</NavLabel>
            <NavItem Href='@To("guide/reviews")' Icon="Lucide.Users">Reviews</NavItem>
            <NavItem Href='@To("guide/shadow-mode")' Icon="Lucide.ShieldCheck">Shadow mode</NavItem>
        </NavMenu>
    </AppSidebar>

    <AppMain>
        <Stack Gap="5" Padding="new() { Base = 4, Md = 10 }" MaxWidth="Length.Prose">
            <Heading Level="1" Size="TextSize.Xl2">Writing your first rule</Heading>
            <TextBlock Color="Color.Muted">A rule reads a decision's inputs and returns an outcome. Start small: one condition, one outcome, and a dry run before it goes live.</TextBlock>
            <Heading Level="2" Size="TextSize.Lg">Conditions</Heading>
            <TextBlock>Each condition compares an input to a value. Conditions in a group must all match; groups are tried in order, and the first group that matches decides the outcome.</TextBlock>
            <Code Block Language="text">@Sample</Code>
            <Alert Title="Run it dry first">A dry run replays last week's decisions through the new rule and shows what would change, without changing anything.</Alert>
        </Stack>
    </AppMain>
</AppShell>

@code {
    // Links within this example, in its own address so they stay here: ?site= for the site's
    // sections, ?view= for the guide's chapters, so each list marks its own current page.
    private string Site(string? section) => Navigation.GetUriWithQueryParameter("site", section);

    private string To(string? page) => Navigation.GetUriWithQueryParameter("view", page);
}

Static: The panel opens without changing the address, focus moves into it, and Escape closes it.

A SiteNav in the header, as here, puts its links at the top of the panel on phones, or at the top of the drawer, so a phone has one menu rather than two.

An icon rail

Use Collapsible to let people shrink the sidebar to an icon rail on wide screens: the menu button collapses and expands it, and each link's text shows as a tooltip.

Preview width

Preview

Code

IconRail.razor
@inject NavigationManager Navigation

@* Collapsible shrinks the sidebar to an icon rail on wide screens: the menu button collapses it,
   labels show as tooltips, and opening a group expands it again. Inset raises the work area as
   one panel in a tinted frame. *@
<AppShell Variant="ShellVariant.Inset">
    <AppHeader>
        <SidebarToggle For="rail" />
        <AppBrand Href="@To(null)" Icon="Lucide.Zap" Label="Acme" />
        <Box Grow />
        <IconButton Icon="Lucide.Search" Label="Search" Variant="ButtonVariant.Ghost" Accent="Accent.Neutral" />
        <IconButton Icon="Lucide.Bell" Label="Notifications" Variant="ButtonVariant.Ghost" Accent="Accent.Neutral" />
    </AppHeader>

    @* Its own Id, so its remembered state (the rail-collapsed cookie) is its own. *@
    <AppSidebar Id="rail" Collapsible>
        <NavMenu>
            <NavItem Href="@To(null)" Icon="Lucide.LayoutDashboard">Dashboard</NavItem>
            <NavItem Href='@To("inbox")' Icon="Lucide.Inbox" Badge="4" BadgeAccent="Accent.Primary">Inbox</NavItem>
            <NavGroup Label="Projects" Icon="Lucide.Folder">
                <NavItem Href='@To("projects/website")'>Website</NavItem>
                <NavItem Href='@To("projects/course")'>Course app</NavItem>
            </NavGroup>
            <NavDivider />
            <NavItem Href='@To("settings")' Icon="Lucide.Settings">Settings</NavItem>
            <NavItem Href='@To("billing")' Icon="Lucide.CreditCard">Billing</NavItem>
        </NavMenu>
        <SidebarFooter>
            <NavMenu aria-label="Account">
                <NavItem Href='@To("account")' Icon="Lucide.CircleUser">Ada Lovelace</NavItem>
            </NavMenu>
        </SidebarFooter>
    </AppSidebar>

    <AppMain>
        <Stack Gap="6" Padding="new() { Base = 4, Md = 8 }">
            <Stack Gap="1">
                <Heading Level="1" Size="TextSize.Xl2">Dashboard</Heading>
                <TextBlock Size="TextSize.Sm" Color="Color.Muted">Deploys and checks across your projects.</TextBlock>
            </Stack>
            <Grid MinChildWidth="Length.Rem(16)" Gap="4">
                <Card Title="Website" Description="Deployed 12 minutes ago from main.">
                    <Row Gap="2"><Badge Status="Status.Success" Dot>Healthy</Badge></Row>
                </Card>
                <Card Title="Course app" Description="Build failed on feature/quizzes.">
                    <Row Gap="2"><Badge Status="Status.Danger" Dot>Failing</Badge></Row>
                </Card>
                <Card Title="Status page" Description="Deployed yesterday from main.">
                    <Row Gap="2"><Badge Status="Status.Success" Dot>Healthy</Badge></Row>
                </Card>
            </Grid>
        </Stack>
    </AppMain>
</AppShell>

@code {
    // Links within this example: ?view= in its own address, so they stay here.
    private string To(string? page) => Navigation.GetUriWithQueryParameter("view", page);
}

Static: The choice is saved in a cookie the server reads, so the page comes back collapsed. Opening a group in the rail expands the sidebar.

With the script, the choice is kept in a cookie named after the sidebar's Id (rail-collapsed here, ib-sidebar-collapsed by default), which the server reads, so the next page arrives collapsed; RememberState="false" turns that off. Collapsed sets how it starts when nothing is remembered. In a shell with no menu button, a chevron in the sidebar's footer row does the collapsing; CollapseButton shows or hides that chevron whatever the header has.

Collapsing from C#

Use @bind-Collapsed on an interactive page to follow the rail in C# and to collapse or expand it, as the focus-mode button does here.

Always Live

Preview width

Preview

Code

BoundCollapse.razor
@inject NavigationManager Navigation

@* On an interactive page @bind-Collapsed follows the icon rail, and C# can collapse or expand it,
   here from a focus-mode button. The rail is a wide-screen thing (on phones the sidebar is a
   drawer), so the button and the state line show from the sidebar's breakpoint up. *@
<AppShell>
    <AppHeader>
        <SidebarToggle For="notes" />
        <AppBrand Href="@To(null)" Icon="Lucide.NotebookPen" Label="Field notes" />
    </AppHeader>

    <AppSidebar Id="notes" Collapsible @bind-Collapsed="_collapsed">
        <NavMenu>
            <NavItem Href="@To(null)" Icon="Lucide.FileText">All notes</NavItem>
            <NavItem Href='@To("starred")' Icon="Lucide.Star">Starred</NavItem>
            <NavItem Href='@To("archive")' Icon="Lucide.Archive">Archive</NavItem>
        </NavMenu>
    </AppSidebar>

    <AppMain>
        <Stack Gap="4" Padding="new() { Base = 4, Md = 8 }" MaxWidth="Length.Prose">
            <Row Gap="3" Align="Align.Center" Wrap>
                <Heading Level="1" Size="TextSize.Xl2" Grow>Site visit, October 14</Heading>
                <Button Variant="ButtonVariant.Outline" Accent="Accent.Neutral" Size="ControlSize.Sm" VisibleFrom="Breakpoint.Md"
                        OnClick="() => _collapsed = !_collapsed">@(_collapsed ? "Show sidebar" : "Focus mode")</Button>
            </Row>
            <TextBlock>Sensor 4 reads two degrees high against the handheld. Recalibrate before the next run, and check whether the casing was opened during transport.</TextBlock>
            <TextBlock Size="TextSize.Sm" Color="Color.Muted" VisibleFrom="Breakpoint.Md">Sidebar: @(_collapsed ? "collapsed" : "expanded")</TextBlock>
        </Stack>
    </AppMain>
</AppShell>

@code {
    private bool _collapsed;

    // Links within this example: ?view= in its own address, so they stay here.
    private string To(string? page) => Navigation.GetUriWithQueryParameter("view", page);
}

Full height

Use FullHeight to run the sidebar the full height of the window, with the header and footer beside it, and ColorScheme="ColorScheme.Dark" for a dark sidebar in a light app.

Preview width

Preview

Code

FullHeight.razor
@inject NavigationManager Navigation

@* FullHeight runs the sidebar from the top of the window to the bottom, with the header beside it.
   The brand moves into SidebarHeader, which lines up with the header, and ColorScheme makes the
   sidebar dark in a light app. *@
<AppShell>
    <AppHeader>
        <SidebarToggle />
        <Breadcrumb>
            <BreadcrumbItem Href='@To("projects")'>Projects</BreadcrumbItem>
            <BreadcrumbItem>Website</BreadcrumbItem>
        </Breadcrumb>
        <Box Grow />
        <Button StartIcon="Lucide.Rocket" Size="ControlSize.Sm">Deploy</Button>
    </AppHeader>

    <AppSidebar FullHeight ColorScheme="ColorScheme.Dark">
        <SidebarHeader>
            <AppBrand Href="@To(null)" Icon="Lucide.Zap" Label="Acme Cloud" />
        </SidebarHeader>
        <NavMenu>
            <NavItem Href="@To(null)" Icon="Lucide.FolderGit2">Projects</NavItem>
            <NavItem Href='@To("deploys")' Icon="Lucide.Rocket">Deploys</NavItem>
            <NavItem Href='@To("domains")' Icon="Lucide.Globe">Domains</NavItem>
            <NavItem Href='@To("logs")' Icon="Lucide.FileText">Logs</NavItem>
            <NavDivider />
            <NavItem Href='@To("team")' Icon="Lucide.Users">Team</NavItem>
            <NavItem Href='@To("settings")' Icon="Lucide.Settings">Settings</NavItem>
        </NavMenu>
        <SidebarFooter>
            <NavMenu aria-label="Account">
                <NavItem Href='@To("account")' Icon="Lucide.CircleUser">Ada Lovelace</NavItem>
            </NavMenu>
        </SidebarFooter>
    </AppSidebar>

    <AppMain>
        <Stack Gap="5" Padding="new() { Base = 4, Md = 8 }">
            <Heading Level="1" Size="TextSize.Xl2">Website</Heading>
            <Grid Columns="new() { Base = 1, Sm = 3 }" Gap="4">
                <Card><Stat Label="Requests today" Value="182,400" Change="8%" Trend="StatTrend.Up" /></Card>
                <Card><Stat Label="Error rate" Value="0.04%" Change="0.01%" Trend="StatTrend.Down" ChangeStatus="Status.Success" /></Card>
                <Card><Stat Label="P95 response" Value="212 ms" /></Card>
            </Grid>
            <Card Title="Recent deploys">
                <DescriptionList>
                    <DescriptionItem Term="12 min ago"><Badge Status="Status.Success" Dot>Ready</Badge> main · Fix footer links</DescriptionItem>
                    <DescriptionItem Term="2 h ago"><Badge Status="Status.Success" Dot>Ready</Badge> main · Add pricing page</DescriptionItem>
                    <DescriptionItem Term="Yesterday"><Badge Status="Status.Danger" Dot>Failed</Badge> feature/search · Type error in search.ts</DescriptionItem>
                </DescriptionList>
            </Card>
        </Stack>
    </AppMain>
</AppShell>

@code {
    // Links within this example: ?view= in its own address, so they stay here.
    private string To(string? page) => Navigation.GetUriWithQueryParameter("view", page);
}

Static: At phone width the drawer opens without changing the address, and focus moves into it. Its close button, Escape or a tap beside it closes it and puts focus back on the menu button; following a link closes it too.

A brand in the SidebarHeader lines up with the header beside it.

Its own words

Use Label for the navigation landmark's name ("Main"), CloseLabel for the drawer's close button ("Close menu") and CollapseLabel for the collapse control ("Collapse sidebar"). SidebarToggle has its own Label ("Open menu"), CloseLabel and CollapseLabel.

Parts

Written inside AppSidebar. The API page lists their parameters.

  • SidebarHeader: pinned to the top of an AppSidebar, above the scrolling content: a brand or workspace switcher.
  • SidebarBody: an AppSidebar's scrolling content with layout props: a column with Gap, Align and Justify, like Stack.
  • SidebarFooter: pinned to the bottom of an AppSidebar, below the scrolling content: an account menu or settings.
  • SidebarToggle: the menu button that opens an AppShell's sidebar below its breakpoint.

Mode support

What works in each mode, AppSidebar
FeatureOffStaticLive
A column beside the page, from its breakpoint upYesYesYes
A drawer or panel on phones, opened and closed by the menu buttonYesYesYes
Focus moves into the drawer and back; Escape closes itNoYesYes
Collapse to an icon railYesYesYes
The collapsed rail remembered on the next pageNoYesYes
Collapsed bound to C#NoNoYes

Off: JavaScript disabled. Static: a static SSR page with IzzyBlazor's script. Live: an interactive page (Server, WebAssembly or Auto).

Accessibility

  • It renders a nav landmark named "Main" (Label). A closed drawer is hidden from keyboard and screen readers.
  • SidebarToggle is a link named "Open menu" (its Label) to the sidebar; with JavaScript it's a button (role="button") that also takes Space and keeps aria-expanded in step.
  • With JavaScript, opening moves focus into the drawer, to its close button ("Close menu", CloseLabel) when it has one, and makes the rest of the shell inert (a Panel sidebar leaves the header usable).
  • Escape, the close button or the backdrop closes it and puts focus back on the menu button.
  • With JavaScript off the drawer still opens and closes, but focus doesn't move and Escape does nothing; the menu and close buttons are plain links, with no aria-expanded, which nothing could keep in step.
  • Collapsible adds a visually hidden checkbox named "Collapse sidebar" (CollapseLabel), checked while collapsed; in the icon rail, link text stays for screen readers.

Parameters

AppSidebar parameters
PropTypeDefault
PositionWhich side it's on.SidebarPositionSidebarPosition.Start
DrawerPositionBelow Breakpoint, the side the drawer slides in from, and the side of the header its menu button moves to.SidebarPosition?computed
SmallScreenHow it opens below Breakpoint: SidebarSmallScreen.Drawer (the default) slides in from the side over the page, for long navigation such as docs or an app; SidebarSmallScreen.Panel opens under the header, full width, with the header still in view and SidebarToggle turned into its close button, as SiteNav does.SidebarSmallScreenSidebarSmallScreen.Drawer
FullHeightRuns the full height of the window, with the header and footer beside it instead of above and below.boolfalse
WidthThe width.Length16rem
BreakpointBelow this width it becomes a drawer.BreakpointBreakpoint.Md
LabelThe navigation landmark's name for screen readers.string?"Main"
IdThe element id, which SidebarToggle links to.string"ib-sidebar"
CollapsibleLets people shrink it to an icon rail on wide screens, with a chevron at its top and SidebarToggle.boolfalse
@bind-CollapsedWhether it is collapsed.boolfalse
CollapsedWidthThe width when collapsed.Length3.5rem
RememberStateRemembers the choice in a cookie (written by the script, read by the server), so it survives reloads and navigation.booltrue
CollapseButtonThe chevron at the top of the sidebar.bool?
CollapseButtonPositionWhere the chevron goes: VerticalPosition.Bottom (the default, in the footer row) or VerticalPosition.Top.VerticalPositionVerticalPosition.Bottom
CloseLabelThe drawer's close button name.string?"Close menu"
CollapseLabelThe collapse control's accessible name.string?"Collapse sidebar"
BgThe background, such as Color.Fill.Color?Color.Surface
ColorSchemeThe color scheme of this part: ColorScheme.Dark for a dark sidebar or header in a light app, ColorScheme.Inverse for the opposite of the page.ColorSchemeColorScheme.Auto
ChildContentThe content.RenderFragment?

See also