DevBits
DevBitsAndroid Handbook
Jetpack Compose

What is state hoisting and why is it useful?

Understand state hoisting as an ownership decision, not just a Compose pattern.

CMP-006Intermediate~30 sec

What the interviewer is testing

This sounds like a Compose pattern question, but the useful part is whether you understand state ownership.

An interviewer is usually trying to see whether you know who should own a piece of state. Sometimes it belongs inside the composable; sometimes it needs to move higher so the rest of the UI can coordinate around it. That decision affects whether there is one source of truth and how reusable the child composable remains.

A strong answer doesn't just repeat "state goes down, events go up." It explains why the state had to move in the first place.

❌ Common mistake

If they ask you to elaborate

Imagine a product screen with a search field and a list of matching products.

It can be tempting to let the search field own its text because that's where the text is displayed:

@Composable
fun SearchField() {
    var query by remember { mutableStateOf("") }

    TextField(
        value = query,
        onValueChange = { query = it },
    )
}

That works while the search field is the only thing that cares about query.

But now the product list needs the same value to decide what to show. If SearchField keeps ownership, the rest of the screen has no clean way to coordinate around that state.

So move ownership to the closest place that needs to coordinate both pieces:

@Composable
fun ProductScreen() {
    var query by rememberSaveable { mutableStateOf("") }

    Column {
        SearchField(
            query = query,
            onQueryChange = { query = it },
        )

        ProductList(query = query)
    }
}

@Composable
fun SearchField(
    query: String,
    onQueryChange: (String) -> Unit,
) {
    TextField(
        value = query,
        onValueChange = onQueryChange,
    )
}

Now ProductScreen owns the query.

SearchField displays the current value and reports what the user did. ProductList can use that same value. There isn't a second copy to keep synchronized.

This is where the familiar Compose pattern comes from:

  • state flows down to the composables that need it;
  • events flow up to the owner that decides what should change.

The important part isn't the direction of the arrows. It's that there is a clear owner making the decision.

That also explains why the "lowest common ancestor" is a useful rule of thumb. If two siblings need the same state, their parent may be the right owner. There is no reason to push it all the way to a screen-level state holder unless the responsibility actually belongs there.

Production thinking

State often starts local and moves upward as a screen gains responsibilities.

A search query might initially affect only a text field. Later it drives filtering, loading, empty states, analytics, or other parts of the screen. At that point the field is no longer the right owner because the decision has become bigger than the field itself.

In production code, I would keep state as close as possible to where it is used, then move it upward when another part of the UI needs to read it, change it, or make decisions from it.

That might mean a parent composable owns simple UI state shared by a few children. If the state is part of screen logic or business rules, a screen-level state holder such as a ViewModel may be the better owner.

The important distinction is that state hoisting does not mean "put state in a ViewModel." It means move ownership to the appropriate owner.

That keeps composable APIs easier to reason about and avoids a common production problem: multiple pieces of mutable state representing the same thing and slowly drifting out of sync.

Follow-up questions

What separates a senior answer

Key takeaways

Learn more

On this page