What it is
Jetpack Compose is Android's declarative UI toolkit, describing interfaces as composable functions of state rather than as mutable view hierarchies.
A @Composable function emits UI from its parameters. When state it reads changes, Compose recomposes just that function rather than mutating a view tree.
Installation
implementation(platform("androidx.compose:compose-bom:2024.09.00"))Getting started
The smallest useful thing you can do with it, and what each part means.
kotlin
@Composable
fun BookList(
books: List<Book>,
selectedId: Int?,
onSelect: (Int) -> Unit, // state comes in, events go out
) {
LazyColumn {
items(books, key = { it.id }) { book ->
ListItem(
headlineContent = { Text(book.title) },
supportingContent = { Text(book.author) },
modifier = Modifier
.clickable { onSelect(book.id) }
.background(if (book.id == selectedId)
MaterialTheme.colorScheme.secondaryContainer
else Color.Transparent),
)
}
}
}Advanced usage
Where the library earns its place over a simpler alternative.
kotlin
@Composable
fun BookScreen(viewModel: BookViewModel = hiltViewModel()) {
// Stops collecting when the screen is not visible — a plain
// collectAsState keeps working in the background and wastes battery.
val state by viewModel.state.collectAsStateWithLifecycle()
// Runs when id changes, cancels the previous run automatically.
LaunchedEffect(viewModel.id) { viewModel.load() }
// Survives recomposition rather than being recreated each time.
val listState = rememberLazyListState()
when (val s = state) {
is UiState.Loading -> CircularProgressIndicator()
is UiState.Error -> ErrorMessage(s.message, onRetry = viewModel::load)
is UiState.Ready -> BookList(s.books, s.selectedId, viewModel::select)
}
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- The UI recomposes constantly
- An unstable parameter — often a lambda or list recreated each pass. Use remember, and prefer immutable types.
- Work restarts on every recomposition
- A side effect ran in the composable body. Move it into LaunchedEffect with the right keys.
Best practices
- Hoist state: composables should take values and emit events, not own mutable state.
- Use collectAsStateWithLifecycle on Android, not collectAsState.
- Provide a stable key in items so list state survives insertions and removals.
- Never perform I/O directly in a composable; use LaunchedEffect or the ViewModel.
Background
Why it exists, and what it was reacting to.
Compose replaced twelve years of XML layouts and findViewById. Google now treats it as the recommended way to build Android UI, and Compose Multiplatform extends it to desktop, iOS and web.
