Dead Buttons in Mobile Apps: Why They Happen and How to Find Them

A dead button — a UI control that looks tappable but does nothing — is one of the most user-corrosive bugs in a mobile app. Users do not report it; they stop using the app. Analytics hide it; the even

February 14, 2026 · 4 min read · Common Issues

A dead button — a UI control that looks tappable but does nothing — is one of the most user-corrosive bugs in a mobile app. Users do not report it; they stop using the app. Analytics hide it; the event fires but no transition follows. Scripted tests miss it because whoever wrote the script never tapped the button just to see if it did something. This guide covers why dead buttons exist and how to systematically find them.

Why dead buttons exist

Unwired handler

The button was added to the layout, the view was inflated, but the click listener was never attached in the Activity / Fragment / VC. The button appears normal; tap has no effect.

Handler attached to wrong view

The listener is on a parent view that intercepts or a sibling that never receives the event. Common with rapidly evolving layouts.

State guard swallows the action

if (!isLoading) { doAction() } — and isLoading is true forever due to a bug.

Feature flag off in production

The action is gated by a flag that was supposed to be on but was not enabled in this environment.

API endpoint 404 or 500, error handler silent

The tap fires, the network call fails, the error handler logs and returns. User sees no feedback.

Null intent / broken navigation

Button triggers navigation to a destination that does not exist or is null in this build.

Permission missing, fail silently

Tap needs camera permission; permission denied; error eaten; no dialog.

Inert decorative element

Styled to look like a button but never intended to be interactive. Worse than dead — misleading.

Detection

Manual exploration

Tour the app deliberately. Tap every button. Note which produce no response. This is what manual QA does well and scripts cannot do cheaply.

Automated exploration

This is where SUSA shines. Every tap is classified by outcome:

Every no_change tap on an interactive element is a dead-button candidate. SUSA tracks dead_buttons per screen and surfaces them in the report.

Scripted tests

Scripted tests do not find dead buttons unless the test script specifically asserts the post-tap state. Most tests assert the happy-path navigation, so a dead button on a secondary action goes unreported.

Telemetry

Tap events without subsequent screen transitions / feature events are indicative. But this requires extensive instrumentation and deliberate analysis.

Prevention

Wire every interactive at add-time

Code review rule: new button, new handler, no merge otherwise.

Linting

Custom lint rule: any @+id/ referenced as Button / MaterialButton without a corresponding listener binding in the same Activity/Fragment — warn.

UI tests with post-tap assertions

Every interactive in a screen test should assert a post-tap observable change — navigation, dialog, snackbar, state update.

Feature flag defaults

Default OFF for new flags in dev is correct; default ON for shipped features when ramping up is also correct. The bug is when production ramp fails to flip the flag and the button ships dead.

Graceful error handling

A tap that fails a network call should not be silent. Minimum: snackbar "Something went wrong, try again." Ideally: retry button, offline mode consideration, issue tracker link.

Fix patterns

Kotlin Activity with ViewBinding:


binding.submitBtn.setOnClickListener {
    if (viewModel.canSubmit.value == true) {
        viewModel.submit()
    } else {
        binding.root.snack("Complete required fields first")
    }
}

Jetpack Compose:


Button(onClick = { viewModel.submit() }, enabled = canSubmit) { Text("Submit") }

Compose forces you to think about enabled state. A button without a disabled variant of UI is suspect.

SwiftUI:


Button("Submit") { viewModel.submit() }.disabled(!canSubmit)

Tracking dead buttons in CI

Run SUSA on every build. Dead-button count is a first-class metric:


Dead buttons (threshold = 1 no-change tap): 3
  - Notifications screen: bell icon (top-right)
  - Settings screen: "Contact Support" row
  - Profile screen: "Edit Cover" overlay

If dead button count increases from previous build, investigate before merging. Fix or document with a TODO pointing to the issue.

How SUSA detects dead buttons

Two layers:

  1. Per-screen no-change taps — any interactive element tapped resulting in no_change outcome is a candidate. Confidence grows with taps (tap once = possible fluke; tap twice with same result = likely dead).
  1. Cross-session knowledge — DeadButton storage persists across sessions for the same app package. Buttons that are dead in session 1 are remembered in session 2; if they are no longer dead, memory is updated; if they are still dead, severity is upgraded.

Both the curious and adversarial personas tap broadly and surface dead buttons. The adversarial persona also repeatedly taps suspicious buttons to catch state-guard-swallows-action bugs.


susatest-agent test myapp.apk --persona curious --steps 100

A typical report flags 3-7 dead buttons in the first exploration of a newly-built app. Fixing them is usually a 2-line change per button. The cumulative UX impact is disproportionate — every user who tapped that button and got nothing is a quiet loss of trust.

Example: SauceLabs MyDemo

Session 127 found a dead button on the product detail screen: tapping the "Compare" icon did nothing. Root cause: the ViewModel had canCompare = true gated on a backend flag that was never enabled for the test environment. The compare icon showed because it was implemented; it was dead because the capability was still behind a kill switch. Fix: hide the icon when canCompare is false, not disable it. The user never sees a dead button.

Dead buttons are easy to fix, easy to miss, and expensive when shipped. Invest 30 minutes per release finding them.

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free