20 May 2025
Navigating between screens in your app should be simple, shouldn't it? However, building a robust, scalable, and delightful navigation experience can be a challenge. For years, the Jetpack Navigation library has been a key tool for developers, but as the Android UI landscape has evolved, particularly with the rise of Jetpack Compose, we recognized the need for a new approach.
Today, we're excited to introduce Jetpack Navigation 3, a new navigation library built from the ground up specifically for Compose. For brevity, we'll just call it Nav3 from now on. This library embraces the declarative programming model and Compose state as fundamental building blocks.
The original Jetpack Navigation library (sometimes referred to as Nav2 as it's on major version 2) was initially announced back in 2018, before AndroidX and before Compose. While it served its original goals well, we heard from you that it had several limitations when working with modern Compose patterns.
One key limitation was that the back stack state could only be observed indirectly. This meant there could be two sources of truth, potentially leading to an inconsistent application state. Also, Nav2's NavHost was designed to display only a single destination – the topmost one on the back stack – filling the available space. This made it difficult to implement adaptive layouts that display multiple panes of content simultaneously, such as a list-detail layout on large screens.
Nav3 is built upon principles designed to provide greater flexibility and developer control:
To give you an idea of how Nav3 works, here's a short code sample.
// Define the routes in your app and any arguments. data object Home data class Product(val id: String) // Create a back stack, specifying the route the app should start with. val backStack = remember { mutableStateListOf<Any>(ProductList) } // A NavDisplay displays your back stack. Whenever the back stack changes, the display updates. NavDisplay( backStack = backStack, // Specify what should happen when the user goes back onBack = { backStack.removeLastOrNull() }, // An entry provider converts a route into a NavEntry which contains the content for that route. entryProvider = { route -> when (route) { is Home -> NavEntry(route) { Column { Text("Welcome to Nav3") Button(onClick = { // To navigate to a new route, just add that route to the back stack backStack.add(Product("123")) }) { Text("Click to navigate") } } } is Product -> NavEntry(route) { Text("Product ${route.id} ") } else -> NavEntry(Unit) { Text("Unknown route: $route") } } } )
To get started, check out the developer documentation, plus the recipes repository which provides examples for:
We plan to provide code recipes, documentation and blogs for more complex use cases in future.
Nav3 is currently in alpha, which means that the API is liable to change based on feedback. If you have any issues, or would like to provide feedback, please file an issue.
Nav3 offers a flexible and powerful foundation for building modern navigation in your Compose applications. We're really excited to see what you build with it.
Explore this announcement and all Google I/O 2025 updates on io.google starting May 22.