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

Reference: