The most simple and efficient way to preview Composable with Hilt in Jetpack Compose
2025-09-17 · by Cactus
Composables in Jetpack Compose should be as DUMB as possible...
When I first used Hilt with Jetpack Compose, all my previews broke. The error messages were confusing, and nothing showed up.
Here’s the problem: Hilt only works with the full app, it does not work with previews -> which only shows a part of your app, it is just for designing the UI => So, that’s why things break.
For example, this crashes in Preview:
@Composable
fun Screen1(viewModel: ViewModelCountDown = hiltViewModel()) {
val focusUiState by viewModel.focusUiState.collectAsState()
// ...
}
The fix? Separate UI from logic. Instead of letting Composable talk to Hilt directly, give it “fake” data in Preview:
@Composable
fun Screen1(
focusUiState = FocusUiState,
restUiState = RestUiState,
onStart: () -> Unit = {}
) {
//code that needs focusUiState, restUiState, onStart()
}
Then, in ViewModel, you pass fake data
@Preview(showBackground = true)
@Composable
fun Screen1Preview() {
Screen1Content(
focusUiState = FocusUiState(),
restUiState = RestUiState(),
onStart = {}
)
}
This way, Composables do not depend on hiltViewModel, or any business logic.
And the real screen uses the ViewModel:
@Composable
fun Screen1(viewModel: ViewModelCountDown = hiltViewModel()) {
val focusUiState by viewModel.focusUiState.collectAsState()
val restUiState by viewModel.restUiState.collectAsState()
Screen1Content(focusUiState, restUiState, viewModel::startCountDown)
}
Lesson learned
Composable should be as dumb as possible. Their only job is to draw UI. They shouldn’t know about Hilt, repositories, or business logic. Keep them simple → and your previews, testing, and maintenance will all be much easier.
For more detail, check my code here: https://github.com/FrankLoSt/Pomodoro/tree/drop-down