Navigation Rail
A navigation rail lets people switch between top-level destinations on larger windows, and expands to show full labels.
<md-gb-nav-rail aria-label="Primary" collapsible>
<md-gb-nav-rail-item selected href="#inbox">
<md-gb-icon slot="icon">mail</md-gb-icon>
Inbox
</md-gb-nav-rail-item>
<md-gb-nav-rail-item href="#chat">
<md-gb-icon slot="icon">chat_bubble</md-gb-icon>
Chat
</md-gb-nav-rail-item>
<md-gb-nav-rail-item href="#spaces">
<md-gb-icon slot="icon">groups</md-gb-icon>
Spaces
</md-gb-nav-rail-item>
<md-gb-nav-rail-item href="#meet">
<md-gb-icon slot="icon">videocam</md-gb-icon>
Meet
</md-gb-nav-rail-item>
</md-gb-nav-rail>Install with shadcn/ui: npx shadcn@latest add https://material-web.dev/3.0/r/md-nav-rail.json (setup)
Usage guidelines
Link to “Usage guidelines”- Use it in medium windows and larger. On compact windows use a navigation bar instead.
- Keep three to seven destinations. Add an optional FAB or menu button in the header.
- Keep it in the same place. The rail stays at the leading edge on every screen of the app.
- Let it expand. A collapsed rail shows icons and short labels, and an expanded rail shows horizontal items and can replace a navigation drawer.
This component is new in M3 Expressive.
Anatomy
Link to “Anatomy”A rail has an optional header, such as a menu button or a FAB, and items. Each item has an icon, a label and an optional badge.
<md-gb-nav-rail aria-label="Mail">
<md-gb-icon-button slot="header" aria-label="Open menu"><md-gb-icon>menu</md-gb-icon></md-gb-icon-button>
<md-gb-fab slot="header" aria-label="Compose"><md-gb-icon>edit</md-gb-icon></md-gb-fab>
<md-gb-nav-rail-item selected href="#anatomy-inbox">
<md-gb-icon slot="icon">inbox</md-gb-icon>
<span slot="badge">3</span>
Inbox
</md-gb-nav-rail-item>
<md-gb-nav-rail-item href="#anatomy-sent">
<md-gb-icon slot="icon">send</md-gb-icon>
Sent
</md-gb-nav-rail-item>
</md-gb-nav-rail>Examples
Link to “Examples”Links
Link to “Links”Give an item an href (and optionally target and rel) to make the destination a link, as in the first example. The item then has the link role and the selected one is marked aria-current="page". Without href an item is a tab that just reports the selection.
Collapsible
Link to “Collapsible”Add collapsible to show a built-in menu button in the header that expands and collapses the rail. To use your own button, put it in the header slot and call toggle(), expand() or collapse(). Set expanded to start expanded. The rail fires toggle when it expands or collapses.
<md-gb-nav-rail aria-label="Mail" collapsible expanded>
<md-gb-nav-rail-item selected>
<md-gb-icon slot="icon">mail</md-gb-icon>
Inbox
</md-gb-nav-rail-item>
<md-gb-nav-rail-item>
<md-gb-icon slot="icon">send</md-gb-icon>
Sent
</md-gb-nav-rail-item>
<md-gb-nav-rail-item>
<md-gb-icon slot="icon">drafts</md-gb-icon>
Drafts
</md-gb-nav-rail-item>
</md-gb-nav-rail>Header
Link to “Header”Use the header slot for a menu button and a FAB above the items.
<md-gb-nav-rail aria-label="Mail">
<md-gb-icon-button slot="header" aria-label="Open menu">
<md-gb-icon>menu</md-gb-icon>
</md-gb-icon-button>
<md-gb-fab slot="header" label="Compose">
<md-gb-icon>edit</md-gb-icon>
</md-gb-fab>
<md-gb-nav-rail-item selected>
<md-gb-icon slot="icon">mail</md-gb-icon>
Inbox
</md-gb-nav-rail-item>
<md-gb-nav-rail-item>
<md-gb-icon slot="icon">send</md-gb-icon>
Sent
</md-gb-nav-rail-item>
</md-gb-nav-rail>Alignment
Link to “Alignment”Set alignment to top (the default), center or bottom to place the items along the rail.
<md-gb-nav-rail aria-label="Primary" alignment="center">
<md-gb-nav-rail-item selected>
<md-gb-icon slot="icon">home</md-gb-icon>
Home
</md-gb-nav-rail-item>
<md-gb-nav-rail-item>
<md-gb-icon slot="icon">search</md-gb-icon>
Search
</md-gb-nav-rail-item>
<md-gb-nav-rail-item>
<md-gb-icon slot="icon">settings</md-gb-icon>
Settings
</md-gb-nav-rail-item>
</md-gb-nav-rail>Badges
Link to “Badges”Set badge on an item to show a count, or badge="dot" for a small dot.
<md-gb-nav-rail aria-label="Mail">
<md-gb-nav-rail-item selected badge="4">
<md-gb-icon slot="icon">mail</md-gb-icon>
Inbox
</md-gb-nav-rail-item>
<md-gb-nav-rail-item badge="dot">
<md-gb-icon slot="icon">chat_bubble</md-gb-icon>
Chat
</md-gb-nav-rail-item>
</md-gb-nav-rail>Secondary items
Link to “Secondary items”Add secondary to an item that should show only while the rail is expanded, such as a less important destination.
<md-gb-nav-rail aria-label="Mail" collapsible expanded>
<md-gb-nav-rail-item selected>
<md-gb-icon slot="icon">mail</md-gb-icon>
Inbox
</md-gb-nav-rail-item>
<md-gb-nav-rail-item secondary>
<md-gb-icon slot="icon">delete</md-gb-icon>
Trash
</md-gb-nav-rail-item>
</md-gb-nav-rail>Modal
Link to “Modal”Add modal so the expanded rail opens over the content with a scrim. Add hide-when-collapsed to take the collapsed rail out of the layout entirely; the app's menu button then calls toggle() to show it. On Android, the back gesture collapses a modal rail.
<md-gb-nav-rail aria-label="Mail" modal collapsible>
<md-gb-nav-rail-item selected>
<md-gb-icon slot="icon">mail</md-gb-icon>
Inbox
</md-gb-nav-rail-item>
<md-gb-nav-rail-item>
<md-gb-icon slot="icon">send</md-gb-icon>
Sent
</md-gb-nav-rail-item>
</md-gb-nav-rail>Appearance
Link to “Appearance”Add divider to draw a line at the rail's edge, elevated to lift it above scrolling content, and narrow to use the 80dp collapsed width.
<md-gb-nav-rail aria-label="Primary" narrow divider>
<md-gb-nav-rail-item selected>
<md-gb-icon slot="icon">home</md-gb-icon>
Home
</md-gb-nav-rail-item>
<md-gb-nav-rail-item>
<md-gb-icon slot="icon">search</md-gb-icon>
Search
</md-gb-nav-rail-item>
</md-gb-nav-rail>Accessibility
Link to “Accessibility”- Give the rail an
aria-labelsuch as "Primary navigation". - Without links the rail is a tab list and its items are tabs, with exactly one selected. Tab moves focus into the rail, the arrow keys move between items, and Enter and Space select one.
- When the items have an
hrefthe rail is a navigation landmark of links and the selected one isaria-current="page". Enter follows a link; Space doesn't. - Every item needs a visible label, which is also its accessible name.
- Give a menu button in the header an
aria-label, such as "Open menu". - A modal rail collapses on Escape and when the scrim is pressed. Keep the menu button available as the single-pointer alternative to swiping it closed.
- Disabled items leave the tab order.
Compatibility
Link to “Compatibility”Material Design 3Spec checked50% tokens
134 of 311 buildable spec units are tested, 11 are excluded with a written reason and 0 are uncovered (the target is 0). 46 of 92 token declarations match the spec (50%), and 46 have no spec token.
navigation-rail on m3.material.io (opens in a new tab) How compliance is measured
BaselineWidely availableChrome 124Edge 124Firefox 125Safari 17.4
The component’s own CSS and JavaScript rely only on web platform features that are Baseline widely available: it works in Chrome 124, Edge 124, Firefox 125, Safari 17.4 and later.
Newer features it uses where the browser has them (progressive enhancements):
- Cursor styles (opens in a new tab) Limited availability degrades safely where it is missing
- user-select (opens in a new tab) Limited availability used inside an
@supportsguard - CloseWatcher (opens in a new tab) Limited availability JavaScript, feature-detected or optional
Browser support Learn more about Baseline (opens in a new tab)
WAI-ARIAaxe and ARIA 1.3 passButtonTabs
Roles the component renders: button (opens in a new tab), navigation (opens in a new tab), tablist (opens in a new tab).
Follows the WAI-ARIA Authoring Practices: Button (opens in a new tab), Tabs (opens in a new tab).
2 rendered states run through axe-core and the WAI-ARIA 1.3 role and attribute rules in a real browser, with no known gaps.
WAI-ARIA 1.3 (opens in a new tab) ARIA Authoring Practices (opens in a new tab) Accessibility
Lighthouse97 · 100 · 100 · 100
Performance Accessibility Best practices SEO
Audits under 90:
- Largest Contentful Paint
- Largest Contentful Paint element
- Elements with visible text labels do not have matching accessible names.
- Eliminate render-blocking resources
- Minify JavaScript
- Reduce unused CSS
- Enable text compression
- Document request latency
- Network dependency tree
- Render blocking requests
Lighthouse 12.8.2 (desktop, 2026-11-08) on the component alone: its first example on a page of its own, without this site's shell. The modules are the development build (not bundled or minified), so the performance score is indicative.
API reference
Link to “API reference”<md-gb-nav-rail>
Link to “<md-gb-nav-rail>”A Material Design navigation rail custom element.
Properties
Link to “Properties”| Property | Attribute | Type | Default |
|---|---|---|---|
alignmentVertical alignment of items ('top', 'center', or 'bottom'). |
alignment |
NavRailAlignment |
'top' |
autoSelectWhether or not to automatically select a tab when it is focused. |
autoselect |
boolean |
false |
collapsibleWhether the navigation rail renders a built-in expand/collapse menu button in the header when no toggle button is slotted into slot="header". |
collapsible |
boolean |
false |
dividerWhether a vertical divider marks the edge of the rail next to the app's content. |
divider |
boolean |
false |
elevatedWhether the rail is elevated to level 1, to distinguish it from content that scrolls underneath it. |
elevated |
boolean |
false |
expandedWhether the navigation rail is expanded to show horizontal items. |
expanded |
boolean |
false |
hideWhenCollapsedHide when collapsed (navigation-rail.md): the collapsed rail is not shown and takes no space, so the body content fills in. The expanded rail (always modal in this mode) appears over the content when the app's menu button calls expand() or toggle(); keep that menu button accessible. |
hide-when-collapsed |
boolean |
false |
itemLayoutThe layout orientation of items ('vertical' or 'horizontal'). |
item-layout |
NavRailItemLayout |
'vertical' |
modalWhether the expanded navigation rail is modal: it opens over the content with a scrim, with modal elevation and shape. |
modal |
boolean |
false |
narrowWhether the collapsed rail uses the narrow width ( md.comp.nav-rail.collapsed.narrow.container.width, 80dp). |
narrow |
boolean |
false |
selectedIndexThe 0-based index of the currently active navigation rail item. Alias for selectedTabIndex. |
selected-index |
number |
|
selectedTabIndexThe index of the currently selected tab. |
selectedtabindex |
number |
Slots
Link to “Slots”| Slot | Description |
|---|---|
| default | Used to display navigation rail items (md-gb-nav-rail-item). |
header |
Used to display optional header controls (menu button, FAB). |
Events
Link to “Events”| Event | Description |
|---|---|
changeEvent |
Fired when the selected item changes. |
toggleCustomEvent<{expanded: boolean, reason?: string}> |
Fired when the expanded state is toggled. |
Methods
Link to “Methods”| Method | Description |
|---|---|
collapse() => void |
Collapses the navigation rail. |
expand() => void |
Expands the navigation rail. |
handleBack() => Promise<void> |
Handles a back request (the Android back gesture or button) on the modal expanded rail: plays the predictive back motion, then collapses the rail. Call it for an in-app back control that should behave like the system back. Resolves after the back motion. |
toggle(force?: boolean) => boolean |
Toggles the expanded state of the navigation rail. |
CSS parts
Link to “CSS parts”| Part | Description |
|---|---|
::part(header) |
The navigation rail's header container. |
::part(items) |
The navigation rail's items container. |
::part(menu-button) |
The built-in expand/collapse menu button when collapsible. |
::part(nav-rail) |
The navigation rail's root container. |
::part(scrim) |
The modal rail's scrim, while expanded. A modal rail expands over the content with a scrim, like Compose ModalWideNavigationRail. Clicking the scrim, pressing Escape or swiping the rail toward its edge collapses it. The scrim (and the menu button of a collapsible rail) are the single-pointer alternatives to the swipe. While a modal rail is expanded, focus moves into it, Tab stays inside it, page scrolling is locked, and focus returns to the previous element when it collapses. The toggle event's detail.reason says why a modal rail collapsed (escape, back, scrim or swipe). Predictive back applies to the modal expanded rail (navigation-rail.md): while it is expanded the rail listens for back requests with a CloseWatcher (the Android back gesture or button, and Escape), falling back to Escape on the document where CloseWatcher is missing. A back request plays the predictive back motion (the rail pops off its edge, Compose ModalWideNavigationRail scales) and collapses the rail. A FAB (md-gb-fab with a label) in the header is an extended FAB while the rail is expanded and an icon FAB while it is collapsed. |
CSS custom properties
Link to “CSS custom properties”| Property | Description |
|---|---|
--collapsed-item-vertical-space |
|
--collapsed-narrow-container-width |
|
--collapsed-top-space |
|
--container-color |
|
--container-elevation |
|
--container-shape |
|
--container-vertical-padding |
|
--container-width |
|
--expanded-container-color |
|
--expanded-container-width-maximum |
|
--expanded-container-width-minimum |
|
--expanded-modal-container-color |
|
--expanded-top-space |
|
--header-space-minimum |
|
--item-active-focused-icon-color |
|
--item-active-focused-label-text-color |
|
--item-active-hovered-icon-color |
|
--item-active-hovered-label-text-color |
|
--item-active-icon-color |
|
--item-active-indicator-color |
|
--item-active-indicator-height |
|
--item-active-indicator-shape |
|
--item-active-indicator-width |
|
--item-active-label-text-color |
|
--item-active-pressed-icon-color |
|
--item-active-pressed-label-text-color |
|
--item-active-state-layer-color |
|
--item-container-height |
|
--item-container-vertical-space |
|
--item-full-width-leading-space |
|
--item-full-width-trailing-space |
|
--item-horizontal-active-indicator-height |
|
--item-horizontal-active-indicator-width |
The expanded destinations' pill width: fit-content (default) hugs the icon and label; 100% takes the destination's width. |
--item-horizontal-icon-label-space |
|
--item-icon-size |
|
--item-inactive-focused-icon-color |
|
--item-inactive-focused-label-text-color |
|
--item-inactive-hovered-icon-color |
|
--item-inactive-hovered-label-text-color |
|
--item-inactive-icon-color |
|
--item-inactive-label-text-color |
|
--item-inactive-pressed-icon-color |
|
--item-inactive-pressed-label-text-color |
|
--item-inactive-state-layer-color |
|
--item-label-text-font |
|
--item-label-text-font-axes |
|
--item-label-text-font-tracking |
|
--item-outer-space |
|
--item-vertical-icon-label-space |
|
--item-vertical-leading-space |
|
--item-vertical-trailing-space |
|
--modal-container-elevation |
|
--modal-container-shape |
|
--modal-scrim-color |
|
--motion-duration |
|
--motion-easing |
<md-gb-nav-rail-item>
Link to “<md-gb-nav-rail-item>”A Material Design navigation rail item custom element.
Properties
Link to “Properties”| Property | Attribute | Type | Default |
|---|---|---|---|
activeWhether the navigation rail item is active. Alias for selected. TODO: replace reflected [active] with :state(selected) |
active |
boolean |
|
badgeOptional badge text (or 'dot' / 'true' for a small dot badge). |
badge |
string |
'' |
disabledWhether the navigation rail item is disabled. |
disabled |
boolean |
false |
hrefThe URL the destination links to. The item is then a link (role link, aria-current="page" while selected) instead of a tab. |
href |
string |
'' |
itemLayoutThe layout orientation of the item ('vertical' or 'horizontal'). |
item-layout |
NavRailItemLayout |
'vertical' |
labelOptional label text if not provided via default slot. |
label |
string |
'' |
relThe link's rel attribute, when href is set. |
rel |
string |
'' |
secondaryWhether the destination is secondary: it shows only while the rail is expanded. |
secondary |
boolean |
false |
tabpanelID of the associated md-aria-tabpanel element. |
tabpanel |
string |
|
targetWhere to open the linked href, e.g. _blank. |
target |
"" |
"_blank" |
unfilledIconWhether the icon has no filled style. The active destination then uses the semibold icon weight instead of the filled icon. |
unfilled-icon |
boolean |
false |
Slots
Link to “Slots”| Slot | Description |
|---|---|
| default | Used to display the item's label text. |
badge |
Used to display a custom badge on the item's icon. |
icon |
Used to display the item's icon (md-gb-icon). |
Methods
Link to “Methods”| Method | Description |
|---|---|
click() => void |
Activates the destination: follows its link, else clicks the tab. |
CSS parts
Link to “CSS parts”| Part | Description |
|---|---|
::part(indicator) |
The active indicator / icon container element. |
::part(label) |
The label text container element. |
::part(nav-rail-item) |
The navigation rail item's presentational container. |