navigation · conformant
NavigationDrawer
NavigationDrawerNavigationDrawerPropsNavigationDrawerVariant
import { useState } from 'react'
import { Button, Icon, NavigationDrawer, Surface, Text } from '@language-lit/material3-expressive'
const items = [
// Selected by default (first item) in every variant below, so its
// `selectedIcon` is visually confirmed across all three.
{
value: 'inbox',
label: 'Inbox',
icon: <Icon source="inbox" />,
selectedIcon: <Icon source="inbox" fill={1} />,
},
{ value: 'sent', label: 'Sent', icon: <Icon source="send" /> },
{ value: 'trash', label: 'Trash', icon: <Icon source="delete" />, disabled: true },
{ value: 'docs', label: 'Docs', icon: <Icon source="description" />, href: '#' },
]
export function NavigationDrawerExample() {
const [dismissibleOpen, setDismissibleOpen] = useState(true)
const [modalOpen, setModalOpen] = useState(false)
return (
<Surface
as="section"
aria-labelledby="navigation-drawer-example-title"
color="surface-container-low"
shape="extra-large"
className="navigation-drawer-example"
>
<Text as="h2" id="navigation-drawer-example-title" variant="titleLarge" emphasis="emphasized">
Navigation drawer
</Text>
<Text as="p" variant="bodyMedium">
Modal (native dialog, slides from the edge), dismissible (collapses
in place), and permanent (always visible) variants.
</Text>
<div className="navigation-drawer-example__row">
<Button variant="outlined" onClick={() => setDismissibleOpen((open) => !open)}>
Toggle dismissible drawer
</Button>
<Button variant="outlined" onClick={() => setModalOpen(true)}>
Open modal drawer
</Button>
</div>
<div className="navigation-drawer-example__frame">
<NavigationDrawer
aria-label="Dismissible example"
variant="dismissible"
open={dismissibleOpen}
onOpenChange={setDismissibleOpen}
items={items}
/>
<NavigationDrawer aria-label="Permanent example" variant="permanent" items={items} />
</div>
<NavigationDrawer
aria-label="Modal example"
variant="modal"
open={modalOpen}
onOpenChange={setModalOpen}
items={items}
/>
</Surface>
)
}NavigationDrawer renders a full-height side panel of navigation items in
one of three variants: a temporary modal overlay, a non-modal panel that
collapses in place, or an always-visible permanent panel.
import { useState } from 'react'
import { Icon, NavigationDrawer } from '@language-lit/material3-expressive'
import '@language-lit/material3-expressive/styles.css'
const [open, setOpen] = useState(false)
<NavigationDrawer
aria-label="Mail folders"
variant="modal"
open={open}
onOpenChange={setOpen}
items={[
{ value: 'inbox', label: 'Inbox', icon: <Icon source="inbox" /> },
{ value: 'sent', label: 'Sent', icon: <Icon source="send" /> },
]}
/>Contract
items,value/defaultValue/onValueChangereuseNavigationBar's ownNavigationItemshape and link-safehrefbehavior.variant(default'modal'):'modal'— a temporary overlay using the same native<dialog>techniqueDialoguses (showModal()/close(), Escape/outside-click dismissal), sliding in from the logical start edge.open/defaultOpen/onOpenChangecontrol it.'dismissible'— a non-modal panel that collapses/expands in place, pushing adjacent content rather than overlaying it. Also controlled byopen/defaultOpen/onOpenChange.'permanent'— always visible;open/defaultOpen/onOpenChangeare accepted but ignored.
Tokens and boundaries
All color, geometry, and motion values live in one
--m3e-comp-navigation-drawer-* registration. Theme overrides remain
scoped to Material3Provider; NavigationDrawer injects no runtime
styles. It imports no Next.js, Vite, router, animation
library, or private application code.