Navigation in Compose: From Basic Routes to Complex Flows

By Engineering Team • Published: 2025-12-08 • Updated: 2025-12-0817 min read

NavigationArchitectureDeep LinksMulti-module

Navigation Compose Fundamentals

Compose Navigation moves away from fragment transactions to a declarative model where destinations are composables. The core components are NavHost, NavController, and NavGraph.

@Composable
fun AppNavigation() {
    val navController = rememberNavController()
    
    NavHost(
        navController = navController,
        startDestination = "home"
    ) {
        composable("home") {
            HomeScreen(
                onNavigateToDetail = { id ->
                    navController.navigate("detail/$id")
                }
            )
        }
        composable(
            route = "detail/{itemId}",
            arguments = listOf(
                navArgument("itemId") { type = NavType.StringType }
            )
        ) { backStackEntry ->
            val itemId = backStackEntry.arguments?.getString("itemId")
            DetailScreen(itemId = itemId)
        }
    }
}

Type-Safe Navigation with Kotlin Serialization

String-based routes are error-prone. Use sealed classes or data classes with Kotlin Serialization for type-safe navigation:

@Serializable
sealed class Screen {
    @Serializable
    data object Home : Screen()
    
    @Serializable
    data class Detail(val itemId: String) : Screen()
    
    @Serializable
    data class Profile(val userId: String, val showEdit: Boolean = false) : Screen()
}

// Usage with type-safe navigation
navController.navigate(Screen.Detail(itemId = "123"))
navController.navigate(Screen.Profile(userId = "456", showEdit = true))

Nested Navigation Graphs

For complex apps, organize related destinations into nested graphs. This improves code organization and enables modular navigation:

NavHost(navController = navController, startDestination = "main") {
    // Main flow
    navigation(startDestination = "home", route = "main") {
        composable("home") { HomeScreen() }
        composable("search") { SearchScreen() }
    }
    
    // Auth flow - separate graph
    navigation(startDestination = "login", route = "auth") {
        composable("login") { LoginScreen() }
        composable("register") { RegisterScreen() }
        composable("forgot_password") { ForgotPasswordScreen() }
    }
    
    // Settings flow
    navigation(startDestination = "settings_main", route = "settings") {
        composable("settings_main") { SettingsScreen() }
        composable("settings_account") { AccountSettingsScreen() }
    }
}

Deep Links Integration

Deep links allow external sources to navigate directly to specific screens. Configure them in your composable destinations:

composable(
    route = "product/{productId}",
    arguments = listOf(navArgument("productId") { type = NavType.StringType }),
    deepLinks = listOf(
        navDeepLink { uriPattern = "https://myapp.com/product/{productId}" },
        navDeepLink { uriPattern = "myapp://product/{productId}" }
    )
) { backStackEntry ->
    ProductScreen(productId = backStackEntry.arguments?.getString("productId"))
}
Note: Remember to declare deep link intents in AndroidManifest.xml for the app to respond to external URLs.
← Back to Blog