Navigation Drawer
Navigation drawers provide ergonomic lateral destination navigation for desktop, tablet, and mobile surfaces.
Navigation drawers show primary destinations along the side of a screen. They help users switch between high-level views, accounts, or multi-level category structures. Designed directly from the Material Design 3 specification, this component provides full-width pill indicators, interactive ripple layers, and responsive standard vs. modal behavior.
Introduction
The MD3 Expressive Navigation Drawer is built for expansive navigation hierarchies. On larger devices (desktops, foldables, and tablets), the drawer can be permanently docked in the layout flow (variant="standard"). On compact screens, it can be displayed as a modal drawer (variant="modal"), sliding over content with a scrim backdrop, escape key handling, and focus trapping.
Anatomy
- Container: The 360px wide panel with
surface-container-lowbackground androunded-e-m3-lg(16px) corner radius. - Headline / Header: App title, brand, or account switcher slot.
- Section Header: Subheadings dividing navigation groups (e.g., "Mail", "Labels").
- Navigation Item: High-touch 56px interactive destination item with full-width pill indicator (
rounded-m3-full). - Icon Slot: 24×24px leading icon with automatic filled state cloning on selection.
- Badge / Counter: Right-aligned counter text (e.g. "24", "100+").
- Divider: Middle-inset horizontal divider separating functional item clusters.
- Footer: Pinned slot at the bottom of the drawer for profile actions or settings.
Variants
Standard Docked Drawer
A persistent drawer docked alongside page content, optimal for desktop and tablet screens.
Modal Drawer
A temporary drawer that slides over content on mobile screens, dismissible via backdrop click, Escape key, or destination selection.
Usage
Basic Usage
import { Icon } from "@bug-on/m3-expressive/core";
import {
NavigationDrawer,
NavigationDrawerContent,
NavigationDrawerDivider,
NavigationDrawerItem,
NavigationDrawerSectionHeader,
} from "@bug-on/m3-expressive/navigation";
export function MyNavigation() {
const [selected, setSelected] = React.useState("inbox");
return (
<NavigationDrawer headline="Mail">
<NavigationDrawerContent>
<NavigationDrawerItem
icon={<Icon name="inbox" />}
label="Inbox"
badge="24"
selected={selected === "inbox"}
onClick={() => setSelected("inbox")}
/>
<NavigationDrawerItem
icon={<Icon name="send" />}
label="Outbox"
selected={selected === "outbox"}
onClick={() => setSelected("outbox")}
/>
<NavigationDrawerDivider />
<NavigationDrawerSectionHeader>Labels</NavigationDrawerSectionHeader>
<NavigationDrawerItem
icon={<Icon name="label" />}
label="Personal"
selected={selected === "personal"}
onClick={() => setSelected("personal")}
/>
</NavigationDrawerContent>
</NavigationDrawer>
);
}
Next.js & Router Integration (asChild)
Use asChild on NavigationDrawerItem to delegate rendering to client routing links such as Next.js <Link>:
import Link from "next/link";
import { Icon } from "@bug-on/m3-expressive/core";
import {
NavigationDrawer,
NavigationDrawerContent,
NavigationDrawerItem,
} from "@bug-on/m3-expressive/navigation";
<NavigationDrawer>
<NavigationDrawerContent>
<NavigationDrawerItem
icon={<Icon name="inbox" />}
label="Inbox"
selected={pathname === "/inbox"}
asChild
>
<Link href="/inbox" />
</NavigationDrawerItem>
</NavigationDrawerContent>
</NavigationDrawer>
Props
NavigationDrawer
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "standard" | "modal" | "dismissible" | "standard" | Display variant of the drawer. |
open | boolean | true (standard) / false (modal) | Controlled open state for modal/dismissible drawer. |
onClose | () => void | — | Callback when drawer should close (backdrop click or Escape). |
autoDismiss | boolean | true | Whether clicking an item automatically closes the modal drawer. |
headline | ReactNode | — | Shortcut string or element for header title. |
header | ReactNode | — | Custom header element at top of drawer. |
footer | ReactNode | — | Element pinned to the bottom of the drawer. |
width | number | string | 360 | Drawer width in pixels or CSS string. |
activeIndicatorTransition | Transition | SPRING_TRANSITION_EXPRESSIVE | Framer Motion transition for active pill indicator. |
NavigationDrawerItem
| Prop | Type | Default | Description |
|---|---|---|---|
selected | boolean | — | Whether this item represents the active destination. |
label | ReactNode | — | Primary destination label text. |
icon | ReactNode | — | Leading icon. Automatically filled when selected. |
activeIcon | ReactNode | — | Distinct icon rendered when selected. |
badge | ReactNode | — | Trailing counter string or badge. |
disabled | boolean | false | Disables interaction and reduces opacity. |
asChild | boolean | false | Delegates rendering to direct child (e.g. Next.js <Link>). |
onClick | () => void | — | Click handler. |