derivedStateOf exists to solve a specific, easy-to-miss performance problem: recomputing a value from other state on every recomposition, even when that computed value hasn’t actually changed.
The Problem It Solves
Say you show a “scroll to top” button only after the user has scrolled past the first item in a list:
val listState = rememberLazyListState()
val showButton = listState.firstVisibleItemIndex > 0
if (showButton) {
ScrollToTopButton()
}
firstVisibleItemIndex changes on every single pixel of scroll, so this composable recomposes constantly — even though showButton only actually flips value twice: once when the user scrolls past item 0, and once when they scroll back. Every other recomposition is wasted work.
Wrapping the Computation
val listState = rememberLazyListState()
val showButton by remember {
derivedStateOf { listState.firstVisibleItemIndex > 0 }
}
if (showButton) {
ScrollToTopButton()
}
Compose still re-evaluates the lambda inside derivedStateOf whenever firstVisibleItemIndex changes — that part isn’t free. But it only triggers recomposition of code that reads showButton when the result of the lambda actually changes. Scrolling from item 5 to item 8 no longer causes ScrollToTopButton‘s parent to recompose, because showButton stayed true the whole time.
When Not to Use It
derivedStateOf has its own overhead, and it only pays off when the input state changes much more often than the derived output does — like the scroll example above. For a cheap computation whose inputs change about as often as the result (for example, combining a first and last name into a full name), a plain calculated value is simpler and often just as fast:
// derivedStateOf is unnecessary overhead here
val fullName = "$firstName $lastName"
Quick Rule
Reach for derivedStateOf when you notice a value is read from rapidly-changing state (scroll position, animation progress, text input on every keystroke) but the value itself changes much less often. If you’re not sure, measure with Layout Inspector’s recomposition counts before adding it — see our guide on debugging recomposition for how.