Common Dead Buttons in Ride Hailing Apps: Causes and Fixes
A dead button is a UI element that looks actionable but does not trigger the expected flow: confirm ride, update pickup, apply promo, cancel trip, call driver, change payment method, or submit a tip.
1. What causes dead buttons in ride hailing apps
A dead button is a UI element that looks actionable but does not trigger the expected flow: confirm ride, update pickup, apply promo, cancel trip, call driver, change payment method, or submit a tip. In ride hailing apps, the issue is often caused by state, timing, or overlay problems rather than a missing click handler.
Common technical root causes:
- Async state races: The button becomes enabled before required data is ready, such as fare estimate, pickup validation, driver availability, or payment authorization.
- Stale session tokens: The user can tap “Request Ride” or “Cancel Ride,” but the API rejects the request because the auth token, trip token, or driver assignment token expired.
- Overlapping UI layers: Map controls, bottom sheets, snackbars, consent banners, promo cards, or accessibility overlays consume touch events.
- Incorrect disabled logic: A button is visually enabled but blocked by a guard condition, such as
if (!canSubmit) return;with no user-facing error. - Backend validation mismatch: The app allows a pickup pin in the UI, but the backend rejects it because it falls outside a service area, geofence, or airport rule.
- Network and offline edge cases: The button sends no request after timeout, airplane mode, DNS failure, or captive portal login.
- Feature flag or remote config bugs: A button is shipped in the UI, but the required backend endpoint, experiment branch, or pricing model is disabled.
- Map gesture conflicts: Tap targets on the map are intercepted by pan, zoom, long-press, or custom marker components.
- Cross-platform bridge issues: In React Native, Flutter, or WebView-based flows, native callbacks may fail silently when a bridge method is missing or misnamed.
- Accessibility tree mismatch: A visual button exists, but screen readers do not expose it as actionable, or its hit area is too small.
Ride hailing apps are especially vulnerable because the same screen often combines maps, pricing, payment, location permissions, real-time driver state, and backend eligibility rules.
2. Real-world impact
Dead buttons directly affect conversion. A user who cannot confirm a ride, apply a promo, or cancel safely may abandon the app and open a competitor.
Typical user complaints:
- “The request button does nothing.”
- “I tapped cancel three times and still got charged.”
- “I can’t select my saved address.”
- “The promo code button is stuck.”
- “I can’t call my driver.”
- “The app says my card is invalid, but there is no way to update it.”
- “The emergency button is not working.”
Business impact:
| Area | Impact |
|---|---|
| Ride requests | Fewer confirmed trips when users abandon at pickup, vehicle, or payment steps |
| Revenue | Lost fare, failed promo conversion, missed surge opportunities |
| Support cost | More calls about duplicate requests, cancellation fees, payment issues, and failed pickups |
| Driver utilization | Empty driver time when riders cannot complete booking |
| Store ratings | One-star reviews often mention “button not working” or “app froze” |
| Safety risk | Non-functional cancel, emergency, call-driver, or share-ETA buttons create real-world danger |
| Trust | Users stop saving payment cards or saved addresses if flows feel unreliable |
For ride hailing, a dead button is not just a UI bug. It can block transportation, money movement, safety actions, and time-sensitive decisions.
3. Specific examples in ride hailing apps
| Example | How it manifests | Likely cause |
|---|---|---|
| Confirm Ride button does nothing | User selects vehicle and taps request, but no loading state, no API call, no error | Race between fare estimate, payment token, and pickup validation |
| Cancel Ride button ignores taps | User taps cancel, but the trip continues or a cancellation fee appears | Cancel button disabled after trip state changes, or API call blocked by stale token |
| Promo Apply button appears active but does not apply code | Button is visible, but promo is not added to fare | Backend promo eligibility mismatch or missing success/error state |
| Call Driver button fails | Button shows phone icon but does not open dialer or masked call | Missing telephony permission, bridge failure, or invalid masked number |
| Edit Destination button is unresponsive | User taps destination row, but bottom sheet does not open | Bottom sheet z-index or pointer event conflict |
| Saved Address button cannot be selected | Saved place appears tappable, but pickup pin does not update | Hit area too small or map marker consumes touch events |
| Emergency / Safety button does not trigger | User taps safety action, but no alert, share, or call flow starts | Feature flag disabled, missing native module, or silent exception in handler |
4. How to detect dead buttons
Start by defining each button’s expected side effect. A button is not “working” because it visually pressed; it works only if the correct flow advances.
What to look for:
- No network request after tap
- No navigation or modal change
- No loading state
- No validation error
- No accessibility action
- No state transition, such as
searching → requested - No analytics event
- Touch target covered by another view
- Button works once, then fails on retry
- Button works on Wi-Fi but fails on weak cellular
Detection techniques:
- Manual QA with device logs: Capture adb logs, Xcode logs, API traffic, and analytics events while tapping each critical button.
- Appium for Android/iOS: Assert that taps trigger navigation, API calls, or state changes.
- Playwright for web and mobile web: Check click handlers, DOM updates, and network responses.
- Network inspection: Verify every important tap sends the expected API call with correct auth headers and payload.
- Accessibility tree checks: Confirm buttons are exposed as actionable, have labels, and meet WCAG 2.1 AA expectations.
- Touch target analysis: Detect controls smaller than 44x44 dp or covered by overlays.
- State machine tests: Verify buttons across trip states: searching, matched, driver arriving, in trip, completed, canceled.
- Failure injection: Test with expired tokens, denied location permission, no network, slow API, invalid pickup, and unavailable vehicle types.
An autonomous QA platform like SUSATest can help here. You upload an APK or provide a web URL, and SUSA explores the app without scripts. It can run persona-based flows such as impatient, elderly, accessibility, business, and power user to catch dead buttons across different interaction patterns. SUSA also tracks flows like login, registration, search, checkout, and ride request with PASS/FAIL verdicts, then generates Appium and Playwright regression scripts for CI.
5. How to fix each example
| Example | Code-level fix |
|---|---|
| Confirm Ride button does nothing | Add explicit loading, error, and disabled states. Do not silently return from onPress. Log the blocked reason and show it to the user. |
| Cancel Ride button ignores taps | Tie the button to the current trip state machine. Allow cancel only in valid states and show a retry or support path when the API rejects it. |
| Promo Apply button fails silently | Return clear promo errors: expired, not eligible, invalid code, or service-area restriction. Disable only after a real validation result. |
| Call Driver button fails | Validate masked number before rendering. Catch permission errors and show “Allow phone access” or fallback in-app calling. |
| Edit Destination button blocked | Audit z-index, modal layering, and pointerEvents. In React Native, ensure parent containers do not capture or block child taps. |
| Saved Address selection fails | Increase hit area to at least 44x44 dp, expose accessibility label, and separate address selection from map marker gestures. |
| Emergency button fails | Treat safety actions as critical paths. Add automated smoke tests, feature flag monitoring, native module checks, and fallback behavior. |
Example pattern:
const handleRequestRide = async () => {
if (isRequesting) return;
try {
setRequesting(true);
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