# Back button behavior is correct for the platform

- **Pattern:** `ab-001861` (`mobile-navigation-linking.navigation-ux.back-button-behavior`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001861
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001861)

## Why it matters

Broken back-button behavior is the most reliably reported UX defect in mobile app reviews. On Android, a hardware back press that skips a screen, crashes the app, or silently does nothing fails WCAG 2.2 SC 2.1.1 (Keyboard), which requires keyboard-equivalent navigation for all functionality. Beyond accessibility compliance, unexpected back behavior corrupts navigation state in ways that can strand users mid-flow — stranded on a checkout confirmation with no path to their cart, or dropped to an empty stack with no way to recover.

## Severity rationale

High because incorrect back-button behavior corrupts navigation state for all Android users and constitutes a keyboard accessibility failure under WCAG 2.2 SC 2.1.1, blocking users who rely on hardware navigation.

## Remediation

React Navigation handles the default back behavior automatically for most stack setups — only intervene when you need to intercept it (e.g., unsaved-form confirmation). Use `useFocusEffect` with `BackHandler` to manage the Android hardware back button on screens that need custom logic:

```tsx
import { useFocusEffect } from '@react-navigation/native'
import { BackHandler, Alert } from 'react-native'

function EditFormScreen({ navigation }) {
  const [isDirty, setIsDirty] = React.useState(false)

  useFocusEffect(
    React.useCallback(() => {
      const onBackPress = () => {
        if (isDirty) {
          Alert.alert('Discard changes?', '', [
            { text: 'Cancel', onPress: () => null },
            { text: 'Discard', onPress: () => navigation.goBack() },
          ])
          return true // prevent default
        }
        return false // use default
      }

      BackHandler.addEventListener('hardwareBackPress', onBackPress)
      return () => BackHandler.removeEventListener('hardwareBackPress', onBackPress)
    }, [isDirty, navigation])
  )
}
```

For screens without custom needs, do not add a `BackHandler` at all — letting React Navigation's default behavior run is correct.

## Detection

- **ID:** `back-button-behavior`
- **Severity:** `high`
- **What to look for:** On Android, check for `useFocusEffect` or `useEffect` handling the hardware back button via `BackHandler` from `react-native`. On iOS, check for proper gesture handling of back swipe. Examine whether back button correctly pops from the current stack and handles nested navigators.
- **Pass criteria:** Count all BackHandler event listeners and back navigation handlers in the codebase. Android back button navigates up exactly 1 level in the stack or exits the app when at the root. iOS back gesture works correctly. No more than 0 unintended screens are popped per back action.
- **Fail criteria:** Back button crashes the app, navigates to an unexpected screen, or is unresponsive. Nested stack back behavior is broken.
- **Skip (N/A) when:** Never — back button behavior applies to all mobile apps.
- **Detail on fail:** Describe the issue. Example: `"Pressing back on Android pops multiple screens instead of one"` or `"Back button on iOS is unresponsive in nested stacks"`
- **Remediation:** Handle back button behavior with React Navigation's built-in support and BackHandler on Android:

  ```tsx
  import { useFocusEffect } from '@react-navigation/native'
  import { BackHandler } from 'react-native'

  function ProductDetailScreen() {
    useFocusEffect(
      React.useCallback(() => {
        const onBackPress = () => {
          // Return true to prevent default back behavior, false to use default
          // Default behavior (return false) pops the current screen
          return false
        }

        BackHandler.addEventListener('hardwareBackPress', onBackPress)

        return () => BackHandler.removeEventListener('hardwareBackPress', onBackPress)
      }, [])
    )

    return <View>{/* screen content */}</View>
  }
  ```

  React Navigation handles most back behavior automatically — only customize if you need special handling (e.g., confirmation dialogs).

## External references

- wcag 2.1.1

Taxons: user-experience

HTML version: https://auditbuffet.com/patterns/ab-001861
