Navigation Compose: what it is, architecture and work in development

Author: IT Sectr Published: 2026-06-29 Reading time: 8 min

Navigation Compose is a Jetpack library for declarative navigation inside Android applications built on Jetpack Compose. Instead of FragmentManager or Intent-based navigation, Navigation Compose offers a single route graph managed through NavController and NavHost. According to Google I/O (2025), Navigation Compose is the recommended navigation method for Compose applications, used in more than 70% of new projects. The library supports typed argument passing, deep links, transition animations and integration with ViewModel via SavedStateHandle.

Key Takeaways

  • Navigation Compose — a Jetpack library for declarative navigation in Compose with a unified route graph
  • NavController — the central controller managing the navigation stack and back stack state
  • NavHost — a container that defines the route graph via composable functions with route
  • Arguments are passed via NavType and SavedStateHandle with typed parameter support
  • Deep Links — navigation through external URLs with automatic argument parsing from URI

What is Navigation Compose?

Navigation Compose is a library from the Jetpack suite that provides a navigation framework for Compose applications. The library is based on the same principles as the Navigation Component for the View system, but is adapted to the declarative nature of Compose: instead of FragmentTransaction, composable functions are used, and the navigation graph is built via Kotlin DSL.

The key difference between Navigation Compose and classic navigation is the absence of FragmentManager. Each screen is a composable function that renders in NavHost when the route matches. The back stack stores not a Fragment, but a record with route, arguments and state. This simplifies the architecture and eliminates lifecycle conflicts typical of Fragment-based navigation.

According to Google (2025), Navigation Compose has gone from experimental to stable and is part of Jetpack since version 2.8.0. The library supports Material3, Type-Safe Navigation (via Kotlin Serialization), nested graphs and modularization. The only limitation is that the library does not support multi-back stack for BottomNavigation without manual configuration, although Google is working on it.

The architecture of Navigation Compose revolves around three entities: NavController (stack management), NavHost (graph container) and NavDestination (individual route with composable). The interaction between them is declarative: the developer describes routes and arguments, and the library handles loading, preserve and restore states.

NavController is the central element of Navigation Compose, managing the navigation stack. It is created via rememberNavController() and passed to NavHost. NavController stores the back stack, the current entry point and supports deferred actions (deeplink after graph initialization).

kotlin
@Composable
fun AppNavigation() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "home") {
        composable("home") { HomeScreen(navController) }
        composable("profile/{userId}") { backStackEntry ->
            ProfileScreen(
                userId = backStackEntry.arguments?.getString("userId") ?: ""
            )
        }
    }
}

Main NavController methods: navigate(route) — navigate to a route, popBackStack() — return to the previous screen, navigateAndClear(route) — navigate and clear the stack. NavOptions specify behavior: launchSingleTop prevents duplicates, popUpTo clears the stack up to the specified route, restoreState restores the previous state.

To access NavController from deeply nested composable functions, use NavHostController via CompositionLocal. LocalNavController is provided in ScopedNavController inside NavHost. Outside NavHost (e.g., in BottomNavigation), the controller is passed via parameters or ViewModel.

NavHost is a composable container that ties NavController to the route graph. Each route is declared via composable(route, arguments, deepLinks), where route is a route string with optional {param} placeholders. When the current route matches, NavHost renders the corresponding composable block.

The route graph is built hierarchically: graphs can be nested via navigation() for grouping routes within a module. Nested graphs have their own startDestination and are combined under a common route prefix. This allows organizing a modular architecture where each feature module registers its own subgraph.

kotlin
NavHost(
    navController = navController,
    startDestination = "main"
) {
    composable("main") { MainScreen(navController) }
    navigation(
        route = "auth",
        startDestination = "auth/login"
    ) {
        composable("auth/login") { LoginScreen(navController) }
        composable("auth/register") { RegisterScreen(navController) }
    }
}

NavHost automatically handles the system Back button (back press) via LocalBackDispatcher. In Material3 Scaffold, it by default picks up NavController for correct BottomNavigation operation. NavHost recreates composable when the route changes, but preserves state via rememberSaveable for input fields and scroll.

Passing arguments between screens

Navigation Compose supports passing typed arguments between screens via route parameters and NavType. Parameters are specified in the route as {paramName} with the type indicated through arguments in composable(). NavType supports String, Int, Long, Float, Boolean, Parcelable and Serializable.

Argument TypeNavTypeRoute Example
StringNavType.StringType"profile/{name}"
IntNavType.IntType"item/{id}"
BooleanNavType.BoolType"settings?enabled={flag}"
ParcelableNavType.ParcelableType"details/{item}"
FloatNavType.FloatType"map?lat={lat}&lng={lng}"

Arguments are extracted from NavBackStackEntry via arguments?.getType(key). For mandatory parameters use defaultValue, for optional — nullable. Parcelable support works only with Kotlin Parcelize or the kotlinx.parcelize library. For complex objects, it is recommended to pass an ID and load data via ViewModel, rather than serializing the entire object.

Since Navigation 2.8.0, Type-Safe Navigation with Kotlin Serialization has been introduced: routes are defined as data classes, and arguments as fields. This replaces string routes with typed objects and eliminates errors in route names. Migration requires the Kotlin Serialization plugin and the navigation-compose-typesafe dependency.

kotlin
@Serializable
/* sealed class Route */
sealed class ProfileRoute(val route: String) {
    data object Home : ProfileRoute("home")
    data class Profile(val userId: String) : ProfileRoute("profile/{userId}")
}

Deep Links is a navigation mechanism that allows opening a specific app screen via URL or intent-filter. In Navigation Compose, deep links are configured via the deepLinks parameter in composable() and are processed automatically when the URI matches the pattern.

A Deep Link is specified as a UriPattern list: "https://example.com/profile/{userId}". URI parameters are automatically mapped to route arguments. NavController processes deep links at app startup (via intent) and during operation (via implicit deep links). To handle pending deep links, use handleDeepLink() in NavController after graph initialization.

According to Google, deep links are recommended for: pushes (Firebase Dynamic Links), email verification, content sharing and cross-navigation from web links. For Android 12+, Digital Asset Links are used to verify deep link authority. AndroidManifest.xml must contain an intent-filter with autoVerify="true" to open links without a dialog.

kotlin
composable(
    route = "profile/{userId}",
    arguments = listOf(navArgument("userId") { type = NavType.StringType }),
    deepLinks = listOf(
        navDeepLink { uriPattern = "https://example.com/profile/{userId}" }
    )
) { backStackEntry ->
    ProfileScreen(userId = backStackEntry.arguments?.getString("userId") ?: "")
}

Limitations of deep links in Navigation Compose: the library does not support deferred deep links — a deep link is processed only after NavHost has fully built the graph. If a deep link arrives before graph initialization, it must be deferred via intent?.data and processed in LaunchedEffect. For Firebase Dynamic Links, use Firebase Dynamic Links SDK together with Navigation Compose.

Transition animations between screens

Navigation Compose supports transition animations via the enterTransition, exitTransition, popEnterTransition and popExitTransition parameters in composable(). Animations are implemented using the Compose Animation API: fadeIn, slideInHorizontally, expandIn and others. By default, animation is disabled — screens are replaced instantly.

Typical animation scenarios: slideInHorizontally for forward navigation (screen slides in from the right), slideOutHorizontally for returning (screen slides out to the right). For BottomNavigation, fade animation without sliding is more commonly used. Animations are set via NavHost and apply to all composables unless individual ones are specified.

kotlin
NavHost(
    navController = navController,
    startDestination = "home",
    enterTransition = { slideInHorizontally() + fadeIn() },
    exitTransition = { slideOutHorizontally() + fadeOut() },
    popEnterTransition = { fadeIn() },
    popExitTransition = { slideOutHorizontally() + fadeOut() }
) { /* composable */ }

Animations can be overridden for each composable individually by passing animation parameters directly in composable(). Importantly, animations should not conflict with the system back press animation. For shared element transition, the accompanist-navigation-animation library or a custom implementation via Modifier.graphicsLayer is required. According to Android Developers (2025), 80% of production applications use slide horizontal animation for standard navigation.

Frequently Asked Questions

How is Navigation Compose different from Navigation Component for View?

Navigation Compose works without Fragment, using composable functions and Kotlin DSL for the graph. Navigation Component (View) is based on FragmentManager and XML graphs. The Compose version is simpler, faster and lacks Fragment lifecycle. Navigation Component for View is only suitable for hybrid applications.

How to pass a complex object between screens?

It is recommended to pass the object ID and load data via ViewModel with SavedStateHandle. If the object is simple, use Parcelable via kotlinx.parcelize. Passing large objects directly via arguments (Bundle) is limited to ~1 MB and may cause TransactionTooLargeException.

Does Navigation Compose support nested graphs?

Yes, via the navigation(route, startDestination) function inside NavHost. Nested graphs have their own startDestination and are combined under a common route prefix. This allows organizing a modular architecture with isolated graphs for each feature module.

How to handle the system Back button in Navigation Compose?

NavController automatically handles back press via BackHandler from Compose. Call navController.popBackStack() when Back is pressed. For custom handling (exit confirmation), use BackHandler(enabled = condition) { callback } before calling popBackStack().

Can Navigation Compose be used with Jetpack Compose Multiplatform?

Currently, Navigation Compose is not supported in Compose Multiplatform. For the iOS part of cross-platform projects, use Voyager or Decompose. Google is working on KMP support, but there is no release timeline. For Android-only projects, Navigation Compose is the only recommended option.

Summary

  • Navigation Compose — a Jetpack library for declarative navigation in Compose, working through a route graph and NavController
  • NavController manages the back stack and provides navigate, popBackStack methods for navigation
  • NavHost ties the controller to the graph via composable(), supporting nested graphs for modular architecture
  • Arguments are passed via NavType with support for String, Int, Parcelable and Type-Safe Navigation with Kotlin Serialization
  • Deep Links are configured via navDeepLink with UriPattern for external navigation from URLs and pushes
  • Animation of transitions is set via enterTransition, exitTransition using the Compose Animation API
  • The library does not support KMP and multi-back stack for BottomNavigation without additional configuration

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also