# Navigation handles both cold-start and warm deep links

- **Pattern:** `ab-001863` (`mobile-navigation-linking.navigation-ux.cold-warm-deeplinks`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001863
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001863)

## Why it matters

When the app is cold-launched from a deep link — the user taps a push notification or a universal link while the app is killed — React Navigation relies on the linking config to seed the initial route. If only warm handling exists, cold launches drop the user on Home with the deep-link payload lost; if only cold handling exists, links received while the app is backgrounded do nothing. Either gap silently breaks re-engagement and violates App Store expectations for universal links.

## Severity rationale

Medium because one of the two deep-link paths works, but half of incoming links fail depending on app state.

## Remediation

React Navigation handles both cold and warm deep links automatically when `linking` is passed to `NavigationContainer`, but test both paths. Add an explicit handler only if you need custom routing logic:

```tsx
import * as Linking from 'expo-linking'

useEffect(() => {
  const sub = Linking.addEventListener('url', ({ url }) => {
    // custom warm-link logic (analytics, auth gate)
  })
  return () => sub.remove()
}, [])
```

Verify with `xcrun simctl openurl booted myapp://product/123` from cold and warm states.

## Detection

- **ID:** `cold-warm-deeplinks`
- **Severity:** `medium`
- **What to look for:** Check the deep link handling. "Cold-start" means the app is launched from a deep link (not already running). "Warm" means a deep link is received while the app is open. Examine the `linking` config for a `fallback` handler. Check whether `useDeepLinkURL` or `addEventListener('url', ...)` is used for warm deep links.
- **Pass criteria:** Count all deep link handling pathways (cold-start via linking config, warm via URL event listener). At least 2 pathways must exist — 1 for cold-start (linking config) and 1 for warm deep links (URL listener or equivalent). Both cases must navigate to the correct screen and pass parameters correctly.
- **Fail criteria:** Deep links only work when the app is already open, or only work on fresh launch. Warm deep links do not trigger navigation.
- **Skip (N/A) when:** The app does not receive deep links (no linking config defined).
- **Detail on fail:** `"Deep links only open the correct screen when the app is already running — cold-start launches go to Home"` or `"App doesn't respond to deep links received while open"`
- **Remediation:** React Navigation handles cold-start deep links via the `linking` config automatically. For warm deep links (while app is running), add a URL listener:

  ```tsx
  const [initialRoute, setInitialRoute] = useState(null)

  useEffect(() => {
    const unsubscribe = navigation.addListener('state', ({ data }) => {
      // Or use: const link = linking.getStateFromPath(url)
    })

    // For deep links received while app is open
    const unsubscribeLinking = linking.onStateChange?.(() => {}) || (() => {})

    return () => {
      unsubscribe()
      unsubscribeLinking()
    }
  }, [])
  ```

  Most deep link handling is automatic with React Navigation if your `linking` config is correct. Test on both cold-start and warm scenarios.

Taxons: user-experience

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