SavedStateHandle in the Navigation 3 Era: It’s Time to Rethink Your ViewModel
If you’ve been developing Android apps with Jetpack Compose for a while, you’ve probably written something like this:
SavedStateHandle in the Navigation 3 Era: It’s Time to Rethink Your ViewModel

Image created by AI
If you’ve been developing Android apps with Jetpack Compose for a while, you’ve probably written something like this:
class DetailViewModel(
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val id: String = checkNotNull(savedStateHandle["id"])
}
For years, this was considered the standard way to retrieve navigation arguments.
But Navigation 3 changes the conversation.
The Hidden Problem with SavedStateHandle
At first glance, SavedStateHandle seems convenient. Your ViewModel gets everything it needs without additional parameters.
However, there’s a subtle architectural issue.
The ViewModel now knows how it was navigated to.
- argument names (“id”)
- key lookups
- navigation contracts
That means your business logic is coupled to your navigation framework.
If someone renames "id” to "userId”, the compiler won’t help you. The mistake only appears at runtime.
The ViewModel shouldn’t care where its data came from.
It should only care about the data itself.
Navigation 3 Encourages a Different Design
Navigation 3 shifts responsibility.
Instead of hiding arguments inside SavedStateHandle, navigation destinations become typed objects that you own.
Imagine a route like this:
@Serializable
data class UserDetail(
val id: String
)
When navigating, you already have a strongly typed object.
Rather than asking the ViewModel to decode it again, simply pass the value directly.
UserDetailScreen(
id = route.id
)
Then inject it into the ViewModel.
class UserDetailViewModel(
private val id: String
) : ViewModel()
Now the ViewModel has exactly what it needs — and nothing more.
No string keys.
No navigation knowledge.
No runtime surprises.
But What About Process Death?
Many developers worry that removing SavedStateHandle means losing process death recovery.
Fortunately, Navigation 3 already solves this.
Your back stack is serializable and can be restored automatically through APIs like rememberNavBackStack().
When the process is recreated, the route object is restored first, and the ViewModel receives the same constructor parameters again. The navigation argument doesn’t need to live inside SavedStateHandle anymore.
In other words:
- Navigation restores navigation state.
- ViewModel owns business logic.
- Each layer has a clear responsibility.
Cleaner Boundaries
This design also improves testing.
Instead of constructing a fake SavedStateHandle:
SavedStateHandle(
mapOf("id" to "123")
)
You simply write:
UserDetailViewModel("123")
The test focuses on business logic rather than framework behavior.
The same applies to previews and dependency injection.
SavedStateHandle Still Has a Place
None of this means SavedStateHandle is obsolete.
It’s still the correct tool for storing temporary UI state that should survive configuration changes or process recreation.
Examples include:
- search queries
- scroll positions
- partially completed forms
- filter selections
These values belong to the current screen state, not to navigation itself.
Navigation arguments and UI state are different concerns, and Navigation 3 finally makes that distinction much clearer.
Final Thoughts
SavedStateHandle wasn’t a bad API.
It solved a real limitation in previous versions of the Navigation library.
But Navigation 3 removes the need to treat navigation arguments as hidden state inside a ViewModel.
Passing dependencies explicitly leads to code that’s easier to understand, easier to test, and less tightly coupled to the navigation framework.
Sometimes the biggest architectural improvements aren’t about adding new APIs.
They’re about removing responsibilities that never belonged there in the first place.
Enjoyed this article? Here’s how you can support my work:
- 👏 Clap (up to 50 times!) if you found this insightful.
- 👤 Follow me and turn on notifications (🔔) so you never miss the next deep dive.
- 📧 Subscribe to get my latest stories delivered directly to your inbox.
You can find all my articles on Android architecture, Jetpack Compose, and Kotlin in this curated reading list.
메타데이터
- post_id
- c581648781d4
- slug
- savedstatehandle-in-the-navigation-3-era-its-time-to-rethink-your-viewmodel-c581648781d4
- url
- https://medium.com/@chanzmao/savedstatehandle-in-the-navigation-3-era-its-time-to-rethink-your-viewmodel-c581648781d4
- canonical_url
- https://medium.com/@chanzmao/savedstatehandle-in-the-navigation-3-era-its-time-to-rethink-your-viewmodel-c581648781d4
- author_url
- https://medium.com/@chanzmao
- status
- ok
- fetched_at
- 2026-06-20 20:29:01