Material 3 Navigation Rail
Overview
Navigation rails provide ergonomic access to primary app destinations.
Material 3 Navigation Rails, using NavigationRailView, support 3–7 destinations in collapsed mode and unlimited items in expanded mode, replacing navigation drawers on tablet and desktop screens. They include optional headers, badges, and labels, adhering to Material Design guidelines. This guide covers implementation, accessibility, and theming.
Using Navigation Rails
- Add Material Components for Android library dependency
- Use
NavigationRailViewwith a menu resource for destinations - Apply
Theme.Material3.*orTheme.Material3Expressive.*theme for styling - Support collapsed (default) and expanded modes
Accessibility
- Set
android:titleon menu items for TalkBack to announce destinations - Use
labelVisibilityModeto control label visibility (LABEL_VISIBILITY_AUTO,SELECTED,LABELED,UNLABELED) - Ensure icons and badges have sufficient contrast
Behavior and Configuration
- Configure menus via XML, add headers (e.g., FAB) or badges
- Handle item selection with
setOnItemSelectedListenerand reselection withsetOnNavigationItemReselectedListener - Use
expand()/collapse()for dynamic behavior - Support submenus in expanded mode
val navigationRail = findViewById<NavigationRailView>(R.id.navigation_rail)
navigationRail.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.alarms -> { /* Handle alarms */ true }
R.id.schedule -> { /* Handle schedule */ true }
R.id.timers -> { /* Handle timers */ true }
else -> false
}
}
navigationRail.setOnNavigationItemReselectedListener { item ->
when (item.itemId) {
R.id.alarms -> { /* Handle alarms reselection */ }
R.id.schedule -> { /* Handle schedule reselection */ }
}
}
Navigation Rail Anatomy
| Component | Description | Collapsed State | Expanded State |
|---|---|---|---|
| Container | 80dp wide (default), 0dp elevation, holds content | Shows 3–7 items | Shows unlimited items, including submenus |
| Header | Optional FAB or logo, 8dp bottom margin | Visible if set | Visible if set |
| Icons/Labels | Destinations with icons (24dp) and optional labels | Limited to 7, labels per labelVisibilityMode |
All items shown, labels typically visible |
| Active Indicator | Highlights active item, 56dp wide, 32dp high | Compact, rounded shape | Expanded width, wraps content |
| Badges | Optional counts/status, large/small variants | Visible if set | Visible if set |
Navigation Rail Example
- Compact navigation for tablet/desktop screens
- Supports badges, headers, and expansion
Example
<com.google.android.material.navigationrail.NavigationRailView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_rail"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:headerLayout="@layout/navigation_rail_fab"
app:menu="@menu/navigation_rail_menu"/>
<!-- res/menu/navigation_rail_menu.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/alarms"
android:enabled="true"
android:icon="@drawable/icon_alarm"
android:title="@string/alarms"/>
<item
android:id="@+id/schedule"
android:enabled="true"
android:icon="@drawable/icon_clock"
android:title="@string/schedule"/>
<item
android:id="@+id/timers"
android:enabled="true"
android:icon="@drawable/icon_sand_clock"
android:title="@string/timers"/>
<item
android:id="@+id/stopwatch"
android:enabled="true"
android:icon="@drawable/icon_stop_watch"
android:title="@string/stopwatch"/>
</menu>
val navigationRail = findViewById<NavigationRailView>(R.id.navigation_rail)
navigationRail.selectedItemId = R.id.schedule
navigationRail.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.alarms -> { /* Handle alarms */ true }
R.id.schedule -> { /* Handle schedule */ true }
R.id.timers -> { /* Handle timers */ true }
R.id.stopwatch -> { /* Handle stopwatch */ true }
else -> false
}
}
val badge = navigationRail.getOrCreateBadge(R.id.alarms)
badge.isVisible = true
badge.number = 99
navigationRail.expand()
Adding Badges
- Show dynamic information like counts or status
- Use large or small badge variants
val navigationRail = findViewById<NavigationRailView>(R.id.navigation_rail)
val badge = navigationRail.getOrCreateBadge(R.id.alarms)
badge.isVisible = true
badge.number = 99
navigationRail.getBadge(R.id.alarms)?.let { badgeDrawable ->
badgeDrawable.isVisible = false
badgeDrawable.clearNumber()
}
Adding Header View
- Add FAB or logo via
app:headerLayoutor programmatically - Positioned at the top of the rail
<com.google.android.material.navigationrail.NavigationRailView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_rail"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:headerLayout="@layout/navigation_rail_fab"
app:menu="@menu/navigation_rail_menu"/>
val navigationRail = findViewById<NavigationRailView>(R.id.navigation_rail)
navigationRail.addHeaderView(findViewById(R.id.fab))
Expanding Navigation Rail
- Expanded mode shows all items, including submenus
- Non-modal, animates with
ChangeBoundstransition
val navigationRail = findViewById<NavigationRailView>(R.id.navigation_rail)
navigationRail.expand()
Attributes and Usage
| Element | Attribute | Related Method(s) | Default Value | Usage Description | Component Type |
|---|---|---|---|---|---|
| Container | app:backgroundTint |
N/A | ?attr/colorSurface |
Sets container background color | All |
| Container | app:elevation |
setElevation |
0dp | Sets container elevation | All |
| Header | app:headerLayout |
addHeaderView, removeHeaderView, getHeaderView |
N/A | Sets optional header view | All |
| Icon | app:itemIconTint |
setItemIconTintList |
?attr/colorOnSecondaryContainer (active), ?attr/colorOnSurfaceVariant (inactive) |
Sets icon color | All |
| Label | app:itemTextColor |
setItemTextColor |
?attr/colorSecondary (active), ?attr/colorOnSurfaceVariant (inactive) |
Sets label text color | All |
| Active Indicator | android:color |
setItemActiveIndicatorColor |
?attr/colorSecondaryContainer |
Sets active indicator color | All |
| Menu | app:menu |
inflateMenu, getMenu |
N/A | Sets menu resource | All |
Styles
- Default style:
Widget.Material3.NavigationRailView - Default theme attribute:
?attr/navigationRailStyle
Theming Navigation Rails
- Customize colors and typography
- Use
res/values/styles.xmlfor theming
Example
<style name="Theme.App" parent="Theme.Material3.*">
<item name="colorPrimary">@color/shrine_pink_100</item>
<item name="colorSecondary">@color/shrine_pink_200</item>
<item name="colorSurface">@color/shrine_pink_50</item>
</style>
<style name="Theme.App" parent="Theme.Material3.*">
<item name="navigationRailStyle">@style/Widget.App.NavigationRailView</item>
</style>
<style name="Widget.App.NavigationRailView" parent="Widget.Material3.NavigationRailView">
<item name="itemTextColor">@color/shrine_pink_900</item>
<item name="itemIconTint">@color/shrine_pink_900</item>
<item name="android:color">@color/shrine_pink_200</item>
</style>
Material Design Documentation
- Adheres to Material Design guidelines for Navigation Rails
- Covers design, behavior, theming specifications
- Includes structure, accessibility, and expressive updates
- Refer to Material Design documentation for full details
FAQ
What is a Material 3 Navigation Rail?
- A vertical navigation bar for tablet/desktop app destinations
How many destinations can it hold?
- Three to seven when collapsed, unlimited when expanded
When to use a Navigation Rail?
- Use for primary navigation on larger screens like tablets/desktops
How to make it accessible?
- Set
android:titlefor TalkBack; adjustlabelVisibilityMode
Can I customize appearance?
- Yes, theme via
res/values/styles.xml
How to add the Material 3 library?
- Include Material Components for Android
Are there updates for the latest standards?
- Reflects latest Material 3 standards, including expressive updates
Tags:
MDC Android








