Navigation 3: High-Level vs. Low-Level NavDisplay APIs
A practical comparison of NavDisplay(backStack) and NavDisplay(entries).

Why NavDisplay(entries) can preserve UI state across multiple NavBackStacks
Navigation 3 provides multiple ways to use NavDisplay.
For a single back stack, this is usually enough:
NavDisplay(
backStack = backStack,
entryProvider = entryProvider {
// ...
}
)
But when each tab has its own NavBackStack, the lower-level entries API gives us more control.
High-Level API: NavDisplay(backStack)
val homeBackStack = rememberNavBackStack(Home)
val searchBackStack = rememberNavBackStack(Search)
val currentBackStack = when (currentTab) {
Tab.Home -> homeBackStack
Tab.Search -> searchBackStack
}
NavDisplay(
backStack = currentBackStack,
entryProvider = entryProvider {
// ...
}
)
This is simple and often all you need.
Low-Level API: NavDisplay(entries)
Instead of letting NavDisplay create the entries, we create decorated entries for each stack:
val homeEntries = rememberDecoratedNavEntries(
backStack = homeBackStack,
entryDecorators = listOf(
rememberSaveableStateHolderNavEntryDecorator()
),
entryProvider = entryProvider {
entry<Home> {
HomeScreen()
}
}
)
val searchEntries = rememberDecoratedNavEntries(
backStack = searchBackStack,
entryDecorators = listOf(
rememberSaveableStateHolderNavEntryDecorator()
),
entryProvider = entryProvider {
entry<Search> {
SearchScreen()
}
}
)
val currentEntries = when (currentTab) {
Tab.Home -> homeEntries
Tab.Search -> searchEntries
}
NavDisplay(
entries = currentEntries
)
Why Does This Matter?
Consider a screen with UI state:
@Composable
fun HomeScreen() {
var selectedId by rememberSaveable {
mutableStateOf<String?>(null)
}
}
When switching tabs:
Home
↓
Search
↓
Home
we want:
Home
└── selectedId = "123"
to remain.
With rememberDecoratedNavEntries(), each back stack can have its own decorated entries:
HomeStack
└── HomeEntry
└── SaveableStateHolder
SearchStack
└── SearchEntry
└── SaveableStateHolder
This gives each navigation entry a place to preserve its rememberSaveable UI state.
The Key Difference
NavDisplay(backStack)
NavBackStack
↓
NavDisplay
↓
NavEntry
NavDisplay(entries)
NavBackStack
↓
rememberDecoratedNavEntries()
↓
NavEntry
↓
NavDisplay
The lower-level API is useful when you want to control how each NavBackStack is converted into decorated NavEntrys.
For multiple independent back stacks, this can help preserve UI state such as:
- scroll position
- selected items
- text input
- expanded/collapsed state
For multiple independent back stacks, NavDisplay(entries) gives each stack its own decorated entries.
This allows rememberSaveable UI state to survive tab switches and be restored after configuration changes or process death.