Jetpack Compose Navigation 3 , Complete Overview

If you've been working with Jetpack Compose, you've probably noticed that navigation always felt like it was fighting against the framework rather than working with it. Well, that's all about to change with Jetpack Navigation 3 (Nav3) - a complete reimagining of how we navigate in Compose applications.
After spending considerable time with Nav3 since its announcement at Google I/O 2025, I'm excited to share this comprehensive overview of what might be the most significant update to the Compose ecosystem this year.
Jetpack Navigation 3 isn't just another incremental update - it's a ground-up rebuild specifically designed for Compose's declarative programming model. Unlike its predecessor, Nav3 embraces Compose state as a fundamental building block, making navigation feel natural and intuitive within the Compose ecosystem.
Currently in alpha, Nav3 represents Google's commitment to creating navigation that truly belongs in the Compose world rather than being retrofitted from the Fragment-based architecture.
Core Philosophy & Design Principles
1. You Own the Back Stack
This is perhaps the most revolutionary change. In Nav3, the developer, not the library, owns and controls the back stack. It's implemented as a simple list backed by Compose state - specifically a SnapshotStateList<T>
where T
can be any type you choose.
Navigation becomes as simple as list manipulation:
Want to navigate forward? Add an item to the list
Want to go back? Remove the last item
State changes are automatically observed and reflected in the UI
2. Get Out of Your Way
Nav3 is designed to be open and extensible, providing building blocks and helpful defaults rather than being a "black box" with inaccessible internal components. No more fighting against the framework's assumptions about how your app should work.
3. Pick Your Building Blocks
Instead of embedding all behavior within a monolithic library, Nav3 offers smaller, composable components that you can combine to create more complex functionality. This modular approach gives you the flexibility to build exactly what your app needs.
Key Features
1. Developer-Controlled Back Stack
The heart of Nav3 is its simple yet powerful back stack management:
kotlin// Define routes with any type
data object Home
data class Product(val id: String)
// Create and manage your back stack
val backStack = remember { mutableStateListOf<Any>(Home) }
// Navigate by adding/removing items
backStack.add(Product("123"))
backStack.removeLastOrNull()
This approach eliminates the complexity of traditional navigation controllers while giving you complete control over your navigation state.
2. NavDisplay & NavEntry System
NavDisplay
is the component that displays your back stack and automatically updates whenever the back stack changes:
@Composable
fun MyApp() {
// Create the backstack with initial destination
val backStack = remember {
mutableStateListOf<Any>(Home) // Start with Home screen
}
NavDisplay(
backStack = backStack,
onBack = {
if (backStack.size > 1) {
backStack.removeLastOrNull()
}
},
entryProvider = { route ->
when (route) {
is Home -> NavEntry(route) {
HomeScreen(
onNavigateToProduct = { productId ->
backStack.add(Product(productId))
},
onNavigateToProfile = { userId ->
backStack.add(Profile(userId))
}
)
}
is Product -> NavEntry(route) {
ProductScreen(
productId = route.id,
onNavigateToSettings = {
backStack.add(Settings)
}
)
}
is Profile -> NavEntry(route) {
ProfileScreen(userId = route.userId)
}
is Settings -> NavEntry(route) {
SettingsScreen()
}
else -> NavEntry(Unit) {
Text("Unknown route: $route")
}
}
}
)
}
3. Scenes for Multi-Pane Layouts
One of Nav3's standout features is the Scenes API - a flexible layout system that allows you to render multiple destinations in the same layout. This is perfect for:
Adaptive layouts: Easy switching between single-pane and multi-pane layouts
Custom scene strategies: Create your own multi-pane arrangements
Material adaptive support: Built-in support for Material list-detail patterns
This makes building responsive apps for tablets and foldables significantly easier than before.
4. Built-in Animations
Nav3 comes with built-in transition animations for destination changes, including support for predictive back gestures:
kotlinNavDisplay(
// Custom animations can be applied
transitionSpec = {
ContentTransform(
fadeIn(tween(300)),
fadeOut(tween(300))
)
}
)
5. State Scoping & ViewModel Support
Nav3 enables state to be scoped to destinations on the back stack, including optional ViewModel support via a dedicated Jetpack lifecycle library.
Entry Decorators provide additional behaviors:
rememberSceneSetupNavEntryDecorator()
— Lifecycle setuprememberSavedStateNavEntryDecorator()
— Saves staterememberViewModelStoreNavEntryDecorator()
— Retains ViewModel
6. Modularity
The API design allows navigation code to be split across multiple modules, improving build times and enabling clear separation of responsibilities between feature modules. This is crucial for large-scale applications.
API Components
NavDisplay Parameters
Parameter | Description |
backStack | The navigation stack |
onBack | Called when system back press occurs |
entryProvider | Converts routes to NavEntry |
decorators | List of NavEntryDecorators for additional behaviors |
transitionSpec | Defines enter/exit animations |
NavEntry Structure
NavEntry
accepts three parameters:
key: The navKey of the screen
metadata (optional): Additional information about screen behavior
content: The Composable screen content
Advanced Features
Custom Scene Strategies
Create custom layouts using a Scene
and SceneStrategy
, enabling complex multi-pane layouts beyond the standard patterns. This gives you the flexibility to create unique navigation experiences tailored to your app's needs.
Conditional Navigation
Switch to a different navigation flow when a condition is met - perfect for authentication flows or first-time user onboarding. This eliminates the need for complex workarounds that were common in Nav2.
Animation Customization
Override the default animations for all destinations or individual destinations, providing fine-grained control over navigation transitions. Your app can have its own unique navigation feel.
Migration from Navigation 2
Key Differences
Aspect | Navigation 2 | Navigation 3 |
Back Stack Ownership | NavController manages it | You directly manage it |
Observable State | Indirect observation | Direct state observation |
Multi-Pane Support | Complex workarounds | Native support |
Architecture | Fragment-based model | Compose-first design |
What's Removed
Traditional NavController pattern
NavHost with single-destination limitation
Complex state hoisting requirements
The migration represents a fundamental shift in thinking about navigation, moving from an imperative to a truly declarative approach.
Recipe Examples
The nav3-recipes
repository provides examples for common use cases:
Basic Navigation
Basic: Most basic API usage
Saveable back stack: With persistent back stack
Entry provider DSL: Using the entryProvider DSL
UI Patterns
Common navigation UI: Navigation toolbar with top-level destinations
Material adaptive: Material list-detail layout
Bottom navigation: Tab-based navigation
Advanced Use Cases
Modularized navigation: Decouple navigation code into separate modules
Hilt injected ViewModel: Navigation arguments passed to ViewModels
Conditional navigation: Authentication flows
Current Status & Future
Nav3 is currently in alpha, which means the API is liable to change based on community feedback. Google plans to provide more code recipes, documentation, and blogs for complex use cases as the library matures.
This alpha status shouldn't deter you from experimenting - the core concepts are solid and the direction is clear.
Benefits Over Navigation 2
Single Source of Truth
Eliminates the "two sources of truth" problem that plagued Nav2, where navigation state could become inconsistent.
Better Compose Integration
Built specifically for Compose patterns, making navigation feel natural within the declarative paradigm.
Adaptive UI Support
Native multi-pane layout support without the complex workarounds required in Nav2.
Flexible Architecture
Modular, extensible components that can be combined to create custom navigation experiences.
Improved Performance
More efficient state management through direct Compose state integration.
Developer Control
Direct back stack manipulation gives you unprecedented control over navigation behavior.
Getting Started
Ready to try Nav3? Here's how to get started:
Add the Navigation 3 dependency to your project
Define your routes using any Kotlin type
Create a back stack with
remember { mutableStateListOf<Any>(startRoute) }
Use NavDisplay with an entryProvider to define your screens
Navigate by adding/removing items from the back stack
The simplicity of this setup compared to Nav2 is remarkable - you can have a working navigation system in just a few lines of code.
The Bottom Line
Navigation 3 represents a fundamental shift in Android navigation, offering a flexible and powerful foundation for building modern navigation in Compose applications. It aligns perfectly with Compose's declarative principles while giving developers unprecedented control over their navigation architecture.
The move from NavController to direct back stack management might seem daunting at first, but once you embrace the paradigm shift, you'll find that navigation becomes much more predictable and easier to reason about.
If you're starting a new Compose project, I'd strongly recommend giving Nav3 a try. For existing projects, consider migrating feature by feature - the modular nature of Nav3 makes this approach very feasible.
The future of Compose navigation is here, and it's built exactly the way we've always wanted it to be - declarative, predictable, and powerful.
What are your thoughts on Navigation 3? Have you started experimenting with it yet? I'd love to hear about your experiences in the comments below!
Subscribe to my newsletter
Read articles from Shivam directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
