MD3
Expressive
MATERIAL DESIGN 3 EXPRESSIVE

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-low background and rounded-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.

Loading demo...

A temporary drawer that slides over content on mobile screens, dismissible via backdrop click, Escape key, or destination selection.

Loading demo...

Usage

Basic Usage

tsx
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>:

tsx
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

PropTypeDefaultDescription
variant"standard" | "modal" | "dismissible""standard"Display variant of the drawer.
openbooleantrue (standard) / false (modal)Controlled open state for modal/dismissible drawer.
onClose() => void—Callback when drawer should close (backdrop click or Escape).
autoDismissbooleantrueWhether clicking an item automatically closes the modal drawer.
headlineReactNode—Shortcut string or element for header title.
headerReactNode—Custom header element at top of drawer.
footerReactNode—Element pinned to the bottom of the drawer.
widthnumber | string360Drawer width in pixels or CSS string.
activeIndicatorTransitionTransitionSPRING_TRANSITION_EXPRESSIVEFramer Motion transition for active pill indicator.
PropTypeDefaultDescription
selectedboolean—Whether this item represents the active destination.
labelReactNode—Primary destination label text.
iconReactNode—Leading icon. Automatically filled when selected.
activeIconReactNode—Distinct icon rendered when selected.
badgeReactNode—Trailing counter string or badge.
disabledbooleanfalseDisables interaction and reduces opacity.
asChildbooleanfalseDelegates rendering to direct child (e.g. Next.js <Link>).
onClick() => void—Click handler.