The Jetpack Navigation Component standardizes navigation in Android apps. Whether you are using Fragments, Compose, or a mix, Navigation gives you a single source of truth for your app's navigation graph, type-safe argument passing, and deep link support out of the box.
Core Concepts
- NavGraph — a graph of destinations and the actions that connect them. Defined in XML or in code (Compose).
- NavController — the engine that drives navigation. One per NavHost.
- NavHost — the container that displays the current destination.
Navigation in Compose
For pure-Compose apps, use the navigation-compose artifact. Define your graph in
a composable function using the NavHost DSL.
val navController = rememberNavController()
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable(
"detail/{itemId}",
arguments = listOf(navArgument("itemId") { type = NavType.StringType })
) { backStackEntry ->
val itemId = backStackEntry.arguments?.getString("itemId")
DetailScreen(itemId = itemId ?: "")
}
}
Type-Safe Navigation (Navigation 2.8+)
Navigation 2.8 introduced type-safe routes via Kotlin serialization — no more stringly-typed route strings.
@Serializable
data class Detail(val itemId: String)
NavHost(navController, startDestination = Home) {
composable<Home> { HomeScreen(navController) }
composable<Detail> { backStack ->
val detail: Detail = backStack.toRoute()
DetailScreen(detail.itemId)
}
}
// Navigate
navController.navigate(Detail(itemId = "abc123"))
Deep Links
Navigation handles deep links with minimal setup. Declare your deep link URI in the composable destination and the system routes incoming intents automatically.
composable<Detail>(
deepLinks = listOf(navDeepLink<Detail>(
basePath = "https://example.com/item"
))
) { ... }
Bottom Navigation and NavGraph Nesting
Each tab in a bottom navigation should have its own nested NavGraph. This preserves back stacks per tab — users expect to return to the same position in each tab they left.
NavHost(navController, startDestination = "home_graph") {
navigation(startDestination = "home", route = "home_graph") {
composable("home") { HomeScreen(navController) }
composable("home_detail") { HomeDetailScreen() }
}
navigation(startDestination = "profile", route = "profile_graph") {
composable("profile") { ProfileScreen() }
}
}
Back Stack Management
- Use
popUpTowithinclusive = trueto clear the back stack when navigating to a new root (e.g., after login). - Use
launchSingleTop = trueto avoid duplicate destinations in the back stack from bottom nav taps. - Never navigate from background threads — always on the main thread.