A @Composable function describes a piece of UI declaratively — it says what the UI should look like given its current inputs, rather than imperatively creating and mutating view objects the way the older Android View system required. mutableStateOf (usually wrapped in remember, so it survives recomposition without resetting) creates observable state; any composable reading that state is automatically recomposed — re-executed to reflect the new value — whenever it changes, with no manual UI-update code needed at all.
A basic composable function
@Composable marks Greeting as something Compose can call as part of building UI — it simply describes a Text element given a name, with no imperative view-creation code anywhere.
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}State and recomposition: the core mental model
Clicking the button changes count; Compose notices Text reads that state, and automatically re-runs (recomposes) just the parts of the UI that actually depend on it — no manual "update the label" code anywhere.
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) } // survives recomposition, resets on real restart
Column {
Text("Count: $count")
Button(onClick = { count++ }) {
Text("Increment")
}
}
}Why remember matters
Without remember, a new mutableStateOf(0) would be created on EVERY recomposition, resetting count back to 0 constantly — remember specifically tells Compose to preserve this value across recompositions, treating it as ongoing state rather than a fresh initial value each time.
// WITHOUT remember: count resets to 0 on every recomposition — genuinely broken
// var count = mutableStateOf(0)
// WITH remember: count survives recomposition, only resets on a true fresh composition
var count by remember { mutableStateOf(0) }