Jetpack Compose UI framework optimizing Android development performance

Key takeaways

Compose performance is still a real production risk in 2026. Misuse of state, lambdas, and unstable types still ship apps at 3.5 FPS on flagship hardware — 450 ms frame times against a 16 ms budget.

Vkompose is the most aggressive line of defense. The VK.com open-source toolkit ships an IntelliJ plugin, a Gradle plugin, and Detekt rules that highlight, log, and block recomposition issues at compile time — not just at runtime.

Strong Skipping Mode covers ~30% of issues for free. Compose Compiler 1.5.4+ skips composables whose parameters are reference-equal even if they are technically unstable — turn it on before doing anything else.

Layered defense beats single tools. Stack Strong Skipping + Vkompose + Detekt + Layout Inspector + Baseline Profiles + Macrobenchmark — each one catches a different class of regression and CI/CD ties them together.

Real teams ship faster after migration. Twitter cut UI boilerplate 10×, Reddit shipped Recap on Compose, R8’s optimize profile cuts ~30% from cold start. Adoption pays back if the performance discipline is in place.

Why Fora Soft wrote this playbook

Fora Soft has shipped Android apps for 17+ years. Around 40% of our active engineering capacity sits in mobile, video, and real-time products — many of them on Jetpack Compose for the last three years. We have run into every recomposition trap on the list below, fixed them in production, and codified the discipline into our internal CI/CD checks.

Vkompose is not our project — it is open source from VK.com’s Android team — but we use it on most Compose-heavy engagements because no other toolkit gives the same combination of compile-time blocking, IDE highlighting, and runtime recomposition logging in one bundle. This article is the practical playbook we hand to our Android engineers when they join a Compose project, paired with the broader performance toolkit (Strong Skipping, Modifier.Node, Baseline Profiles, R8 optimization) that makes Vkompose pay off.

If you are scoping or auditing an Android product on Compose — whether the buyer is a CTO worrying about jank, a product owner negotiating a launch deadline, or a founder picking a partner to ship the v1 — this guide is the same one we would walk you through on a call. We use Agent Engineering on every project to compress the calendar and the cost; that’s why our quotes typically land below the industry baseline.

Compose app feeling janky?

Get a 30-minute Compose performance review with our Android leads — we will scan the codebase for the top recomposition traps and give you a concrete remediation plan.

Book a 30-min call → WhatsApp → Email us →

Why Compose performance still hurts in 2026

Jetpack Compose is the default Android UI toolkit and the language of every modern Android team — Google’s January 2024 release alone landed roughly a 20% scroll-FPS bump and a 12% startup improvement. The platform keeps getting faster. The applications keep getting slower.

The reason is that Compose’s declarative model trades raw control for ergonomic state management — and ergonomic state management is easy to misuse. Documented production cases have shown Compose feeds running at 3.5 FPS (450 ms per frame) on flagship devices because of one unstable List parameter that triggered 47 recompositions per scroll gesture. None of that shows up in code review; it only surfaces under Layout Inspector or under user complaints.

The fix is not heroic optimization. It is a layered defense: Strong Skipping Mode, Vkompose for compile-time and runtime checks, Detekt rules for code-smell coverage, Layout Inspector for spot debugging, Baseline Profiles for AOT, and R8 Full mode for everything else. Each tool catches a different class of regression. Stacked together, they take a production Android team from “hope it’s smooth” to “CI/CD blocks janky merges.”

The seven recomposition traps every Compose team falls into

If you are auditing a Compose codebase, scan for these seven patterns first — they cover roughly 90% of our remediation work.

1. Unstable types as parameters. Collections (List, Set, Map) are always unstable to the Compose compiler. Data classes are unstable unless every property is too. Cross-module models are unstable if the producing module isn’t built with Compose. Annotate with @Stable/@Immutable, switch to ImmutableList from Kotlinx-Immutable, or use a stability config file.

2. Lambdas captured in unstable scope. Inline lambdas allocated each recomposition recompose every child that takes them. Pass method references (::onClick) or use remember { { … } } for stable identity.

3. Recomposition scope too wide. Reading state at the wrong level invalidates the whole subtree. Push state reads down to the smallest leaf composable that needs them; pass lambdas instead of values when a leaf needs to mutate state.

4. Missing keys in LazyColumn/LazyRow. Without stable keys Compose treats every reorder as a new item, discarding remembered state and re-running. Always pass key = { it.id }.

5. Side effects in the composable body. DB queries, file I/O, JSON parsing inside a composable run every recomposition. Wrap in remember { … } or LaunchedEffect.

6. derivedStateOf misuse. Using it where a plain value would do adds overhead; not using it where you should triggers cascading recompositions. Reach for it when a derived value depends on multiple state reads but doesn’t need to recompose every time the source changes.

7. Modifier.composed instead of Modifier.Node. The composed factory rebuilds modifiers on every recomposition; the Node API survives them. Migrating performance-critical custom modifiers gives a 3×–10× speedup on benchmark micro-tests.

Reach for an external code audit when: the team can list the symptoms (jank, ANR, scroll stutter) but not the cause, or the codebase predates Strong Skipping (Compose Compiler < 1.5.4). Our code-auditing playbook explains the diagnostic flow we run.

Read the compiler reports before you write fixes

The Compose compiler can emit two text reports that tell you exactly which classes are unstable and which composables are skippable. Turn them on first; everything else is hypothesis.

tasks.withType<KotlinCompile>().configureEach {
  compilerOptions {
    freeCompilerArgs.addAll(
      "-P", "plugin:androidx.compose.compiler.plugins.kotlin:" +
        "reportsDestination=${'$'}{buildDir}/compose_metrics",
      "-P", "plugin:androidx.compose.compiler.plugins.kotlin:" +
        "metricsDestination=${'$'}{buildDir}/compose_metrics"
    )
  }
}

After the next build, compose_metrics/<module>-classes.txt lists every class as Stable, Immutable, or Unstable; composables.txt shows which functions are Restartable and Skippable. The first audit pass is just reading these and prioritising the screens whose top composables fail to skip.

Vkompose — what each plugin actually does

Vkompose is a bundle of three independently-installable Compose-performance tools maintained by VK.com’s Android engineering team. The bundle solves the same family of problems Layout Inspector does — but with stronger blocking, more flexible logging, and CI/CD-friendly outputs.

1. The IntelliJ IDEA / Android Studio plugin

Highlights composables that are likely to recompose unnecessarily — missing @Stable annotations, unstable parameters, miss-marked composable functions. The IDE colours warn at the level of the function declaration, so junior engineers see the issue while writing the code, not a sprint later.

2. The Gradle plugin (the strict one)

Runs at compile time and fails the build when an unoptimized composable would be produced. This is the difference between a tip and an enforcement. The plugin also ships RecomposeLogger, an opt-in runtime utility that prints exactly which parameter changed when a function recomposed.

// build.gradle.kts (module)
plugins {
  id("com.vk.vkompose") version "0.5"
}

vkompose {
  skippabilityCheck = true       // fail build on unskippable @Composable
  recompose {
    isHighlighterEnabled = true  // visual border around recomposing nodes
    isLoggerEnabled = true       // logs reason of every recomposition
  }
  testTagsCheck = true           // require testTag in semantic UI
}

3. The Detekt rules

Static-analysis rules that fit into your existing Detekt pipeline. They catch missing parameter types, unstable parameter shapes, and the small set of code-smell patterns that Vkompose’s compiler-level checks deliberately skip.

Vkompose vs. Layout Inspector vs. Rebugger vs. Detekt rules

There are at least four ways to track Compose performance issues. They are not interchangeable.

Tool Catches Blocks build? Best for
Layout Inspector Recompose / skip counts, hierarchy No Spot debugging on emulator only
Rebugger Per-component recomposition reasons No Lightweight ad-hoc tracing
Detekt rules for Compose Code-smell patterns (Modifier reuse, parameter naming) Yes (Detekt threshold) Style consistency in CI
Vkompose Stability mistakes, unskippable composables, runtime recomposition causes Yes (Gradle plugin) Production-grade enforcement + live debugging

Reach for Vkompose when: you have a Compose codebase larger than ~50 screens, a CI/CD pipeline that needs to reject performance regressions, and an Android team big enough that runtime debugging by hand does not scale. For a 1–2-person hobby app, Layout Inspector + the compiler reports are usually enough.

Strong Skipping Mode — the cheapest 30% win

Compose Compiler 1.5.4 introduced Strong Skipping Mode and 2.0+ moved it to production-ready. With Strong Skipping enabled, composables whose unstable parameters are reference-equal (===) are skipped instead of always re-running — even if the parameter type itself is technically unstable.

// build.gradle.kts
android {
  composeOptions {
    enableStrongSkippingMode = true
  }
}

Internal benchmarks — both Google’s and the ones we have run on client codebases — show roughly 30% of recomposition issues evaporate without code changes once Strong Skipping is on. It is also the cheapest, fastest mitigation in the toolkit. Turn it on before you start writing @Stable annotations.

Need a Compose performance audit?

Our Android leads will run Vkompose, Layout Inspector, Macrobenchmark, and the compiler reports against your codebase — you get a punch list with effort estimates in 7–10 days.

Book a 30-min audit call → WhatsApp → Email us →

Layout Inspector — for the bug you can already reproduce

Once a screen feels janky on a real interaction, Layout Inspector is the fastest tool to find the offending composable. Open Tools → Layout Inspector, attach to your debug build, and enable “Show Recomposition Counts” in the component-tree options.

Two columns appear on every node: Recompose (how many times the function ran) and Skip (how many times Compose chose to skip). A high Recompose-to-Skip ratio — especially under interactive load — is the smoking gun. Layout Inspector ships in Android Studio so it is the default starting point; Vkompose’s runtime highlighter complements it on physical devices.

Baseline Profiles and Macrobenchmark — how we prove it

Eyeballing FPS does not survive a CI pipeline. The combination that does is Baseline Profiles + Macrobenchmark: ship pre-compiled hot paths to cut cold-start by ~30%, then measure P95 frame latency in a reproducible test.

// :baseline-profile module
@RunWith(AndroidJUnit4::class)
class FeedScrollBenchmark {
  @get:Rule val rule = MacrobenchmarkRule()

  @Test fun feedScrollFrames() = rule.measureRepeated(
    packageName = "com.example.app",
    metrics = listOf(FrameTimingMetric()),
    iterations = 10,
    startupMode = StartupMode.COLD
  ) {
    pressHome()
    startActivityAndWait()
    device.findObject(By.res("feed")).fling(Direction.DOWN)
  }
}

Run it nightly. Fail the pipeline if P95 frame time creeps over 16 ms. Pair it with a Baseline Profile generator that records a critical user journey; the AOT-compiled output is what gives the 30% startup uplift Google quotes.

Migrate Modifier.composed to Modifier.Node

Modifier.composed { … } rebuilds a fresh modifier instance every time the parent recomposes, allocating along the way. The Modifier.Node API survives recompositions, holds state directly, and benchmarks at 3×–10× the speed.

// Old — avoid
fun Modifier.brandRipple() = composed {
  val state = remember { mutableStateOf(0) }
  drawWithContent { /* ... */ }
}

// New — prefer
private class BrandRippleNode : Modifier.Node() {
  var state = mutableStateOf(0)
  // hooks here survive recompositions
}
fun Modifier.brandRipple(): Modifier =
  this then BrandRippleElement

Audit every custom modifier in the codebase. Migrate the ones in lists, in animations, and in complex layouts first — that’s where the allocations stack up.

R8 Full mode + a stability config file

Two configuration changes routinely move metrics by 20–30% with zero source changes.

1. R8 Full mode. Switch proguard-android.txt to proguard-android-optimize.txt and enable android.enableR8.fullMode=true. Google reports ~30% faster startup and ~25% fewer ANRs on representative apps.

2. A stability configuration file. Annotate third-party types you can’t change as stable in a single configuration file (stability_config.conf). Compose treats them as @Stable at compile time without monkey-patching the dependency.

CI/CD — turning the toolkit into a guard rail

A toolkit only matters if a build fails when it should. Wire Vkompose, Detekt, and Macrobenchmark into your pipeline so every PR proves it does not regress.

// .github/workflows/compose-perf.yml
name: Compose performance gate
on: [pull_request]
jobs:
  perf:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./gradlew detekt
      - run: ./gradlew :app:assembleRelease   # Vkompose Gradle plugin
      - run: ./gradlew :baseline-profile:macrobenchmark
      - run: ./tools/check-frame-budget.sh    # fail if P95 > 16 ms

Production case studies — what well-run Compose teams ship

Twitter reduced UI component boilerplate by ~10× after their full Compose migration, alongside faster bug fixing and more flexible theming for A/B testing. Reddit shipped “Recap” on Compose, leaning on its animation engine instead of nested ConstraintLayout. Square / Cash App have publicly committed to Compose for new surfaces and report comparable engineering velocity gains internally. Google’s January 2024 release notes alone documented ~20% faster scroll FPS and ~12% faster startup — gains every team inherits for free with a Compose Compiler upgrade.

Compose Multiplatform — where Vkompose stops

Vkompose ships as Android Gradle plugin + Detekt rules and is Android-only. Teams using Compose Multiplatform for desktop, iOS, or Web will not get Vkompose enforcement on those targets. Plan a separate profiling strategy: Macrobenchmark/Compose Multiplatform compiler reports on iOS, Compose Web Lighthouse, desktop benchmark harness via JFR.

Cost of fixing Compose performance — what to expect

When Fora Soft picks up an audit-and-fix engagement on a Compose product, the typical scope and timeline below is what we quote.

Engagement What is included Indicative range Calendar
Compose performance audit Compiler reports, Vkompose run, Layout Inspector traces, prioritized punch list $6K–$12K 7–10 days
Tier-1 fixes Strong Skipping, key parameters, lambda capture cleanup, R8 Full mode $10K–$25K 2–3 weeks
Tier-2 refactor State-shape redesign, derivedStateOf cleanup, Modifier.Node migration $25K–$60K 4–8 weeks
CI/CD hardening Vkompose Gradle plugin, Detekt rules, Macrobenchmark in CI, perf budgets $8K–$15K 1–2 weeks

A decision framework — pick a Compose performance plan in five questions

1. What Compose Compiler version are you on? Below 1.5.4 → upgrade first; that alone unlocks Strong Skipping. Above 2.0 → you are eligible for the full toolkit.

2. How many screens? Below ~20 → compiler reports + Layout Inspector are enough. Above 50 → install Vkompose Gradle plugin and ratchet CI checks on.

3. What is the user-visible symptom? Cold start → Baseline Profiles + R8 Full. Scroll jank → Vkompose + state hygiene. Animation stutter → Modifier.Node + GPU profiling.

4. Cross-platform footprint? Android-only → full Vkompose stack. Compose Multiplatform → layered approach with platform-specific profilers per target.

5. Where is performance enforced today? Nowhere → start with Detekt + Vkompose Gradle in CI before doing source fixes. Already in CI → tighten budgets and add Macrobenchmark.

Pitfalls we have watched Compose teams fall into

1. Annotating everything as @Stable. Lying to the compiler hides recompositions instead of fixing them. The crash arrives later and it is harder to debug.

2. Skipping Strong Skipping “until we’re ready”. It is production-ready and free. Turning it on later means you will write annotations you didn’t need to.

3. Treating Layout Inspector as a CI tool. It is interactive only. Wire Macrobenchmark and Vkompose Gradle into the pipeline; reserve Layout Inspector for spot debugging.

4. Refactoring state without measuring first. Speculative state refactors often regress something else. Always pull a Macrobenchmark baseline before, after, and a week later.

5. Ignoring R8 Full mode. ~30% startup gains for free are not optional in 2026. Verify the obfuscation rules don’t break your reflection-heavy dependencies and ship it.

KPIs — what to measure and what to budget

Quality KPIs. P95 frame time < 16 ms during scroll, time-to-first-frame < 500 ms cold, ANR rate < 0.1% of sessions, recompose:skip ratio < 1.5 on your hot screens.

Business KPIs. Crash-free sessions over 99.5%, install-to-day-1 retention movement after each performance ship, Play Store Vitals “bad behaviours” under the bad-behaviour threshold (currently 0.47% slow frames).

Reliability KPIs. CI gate uptime, Macrobenchmark variance under 5% across runs, time-to-bisect a regression under one hour. Without a stable benchmark you cannot defend a performance budget.

When NOT to invest in heavy Compose tooling

A solo developer on a 5-screen MVP, a hobby project, or a feature flag the company is days away from killing — none of them earn the cost of standing up Vkompose, Detekt, and Macrobenchmark in CI. For those, ship Strong Skipping plus the compiler reports and move on.

Investment becomes obvious when the product crosses ~50 screens, the team grows past ~5 Android engineers, or a VIP customer reports jank. Past those thresholds, Vkompose+CI pays for itself in a sprint.

Want a punch list, not theory?

Send your repo — we will run Vkompose, Detekt, and Macrobenchmark and return a prioritized fix plan with effort estimates inside ten days.

Book a 30-min call → WhatsApp → Email us →

FAQ

Is Vkompose maintained by Fora Soft?

No. Vkompose is open-source from VK.com’s Android team. Fora Soft uses it on Compose-heavy engagements because it is the most aggressive open-source toolkit for blocking unoptimized Compose code at compile time.

Should we enable Strong Skipping Mode now or wait?

Now. It is production-ready in Compose Compiler 2.0+, fixes about 30% of recomposition issues without code changes, and is the cheapest mitigation in the toolkit. Turn it on before any annotation work.

How does Vkompose compare to Rebugger?

Rebugger is a runtime logging library — lightweight and good for ad-hoc tracing. Vkompose is a full bundle — IDE plugin, Gradle plugin, Detekt rules — that can fail builds when unoptimized code is detected. They are complementary; we use both on large codebases.

Does Vkompose work with Compose Multiplatform?

No. Vkompose is Android-only because it integrates with the Android Gradle plugin. Cross-platform Compose teams need to layer in Macrobenchmark, Compose Multiplatform compiler metrics, and platform-specific profilers per target.

What is the cheapest single change for Compose startup time?

Switching to proguard-android-optimize.txt with R8 Full mode and shipping a Baseline Profile generated from your top user journey. Together they consistently buy 25–35% off cold start.

How much does a Compose performance audit cost at Fora Soft?

$6K–$12K for a full audit (compiler reports, Vkompose run, Layout Inspector traces, Macrobenchmark baseline, prioritized punch list with effort estimates). Most clients then commission Tier-1 fixes which run $10K–$25K.

Is Compose ready for large-scale production teams?

Yes. Twitter, Reddit, and Square run Compose at scale. Tooling (Vkompose, Detekt, Layout Inspector, Macrobenchmark, Baseline Profiles) is mature enough for 100+ engineer teams. The bar today is performance discipline, not framework readiness.

Does Fora Soft work on Android-only or full mobile stacks?

Both. We ship Android, iOS, KMP, and Compose Multiplatform projects. See our 2024 mobile development highlights for representative work.

Android performance

10 ways to optimize Android apps for smooth video streaming

Adjacent performance disciplines — codecs, threading, memory — that pair with Compose hygiene.

Audit playbook

What is code auditing and how to conduct it

The audit framework we use before any optimization sprint — not just for Compose.

Android engineering

Android WebRTC screen sharing — the complete implementation guide

A worked example of an Android feature whose performance budget is unforgiving.

Cost

Mobile app development costs — the 2025 guide

How an Android engagement is priced — including audit-and-fix scopes like the one above.

Ready to make Compose fast and keep it fast?

Jetpack Compose is fast enough for production — when teams keep state shape clean, lambdas stable, and recomposition scope tight. The toolkit to enforce that discipline exists today: Strong Skipping, Vkompose, Detekt, Layout Inspector, Modifier.Node, Baseline Profiles, R8 Full. Stack them, wire them into CI, and your Android product stops trading performance for declarative ergonomics.

If you want the discipline applied to your codebase — or a punch list of what to fix first — book a 30-minute call. We will scan, score, and quote a fix plan with effort estimates in ten days, not ten weeks.

Talk to our Android engineering leads

Book a 30-minute call. We will walk Vkompose, Strong Skipping, R8 Full, and the rest of the playbook against your codebase.

Book a 30-min call → WhatsApp → Email us →

  • Development