"Inline callbacks in Composables are killing my performance."
This is a widespread misconception.
Lambdas can allocate, but the cost is often vastly overstated.
Non-capturing lambdas are cheap, and capturing lambdas only become interesting when they are recreated often enough to matter. More importantly, recent versions of Compose handle this case much better than old advice suggests.
Historically, an inline callback such as onClick = { viewModel .select(id) } would often create a new lambda instance during recomposition.
The concern was not the allocation itself, but the fact that a new lambda instance could make parameter comparisons fail, preventing child composables from being skipped even when nothing meaningful had changed.
With strong skipping enabled, the Compose compiler automatically remembers many lambdas passed to composable parameters.
An inline callback like onClick = { viewModel .select(id) } can be treated as if it had been wrapped in remember{}, using the values it captures as keys.
As long as those captured values remain the same, the callback keeps a stable identity across recompositions, making it much easier for Compose to skip work that doesn't need to happen.
In most apps, lambda allocations are nowhere near the top bottleneck. Don't rewrite clean, readable code based on a vague fear of allocations if you haven't measured first.