All 20 checks with why-it-matters prose, severity, and cross-references to related audits.
Without a single NavigationContainer at the root, React Navigation has no scene graph to mount — screens render as disconnected components, deep links resolve to nothing, and the back button bubbles to the OS and closes the app. Users who tap a push notification or share-sheet link land on a blank route or a cold Home, destroying the user-experience contract the rest of the app depends on. Duplicated containers create competing state trees that silently drop navigation events.
Why this severity: Critical because a missing or duplicated NavigationContainer breaks every downstream routing, linking, and state-restoration feature app-wide.
mobile-navigation-linking.navigation-structure.nav-initSee full patternA screen that appears in the `linking` config but is not registered as `<Stack.Screen>` or `<Tab.Screen>` is a dangling reference — React Navigation logs a warning and refuses to navigate, so deep links, push-notification taps, and `navigation.navigate('X')` calls silently fail. Orphaned registrations with undefined components crash the tree on mount. The user sees a frozen button, a broken link, or a white screen with no recovery path.
Why this severity: High because unregistered screens break navigation for real users at runtime without producing a loud crash.
mobile-navigation-linking.navigation-structure.screens-registeredSee full patternA flat or tangled navigator hierarchy breaks the mental model users have of mobile apps: tabs that don't own their own stack lose history when switched, modals that sit inside a regular stack trap the user behind a back button that dismisses the wrong screen, and competing root navigators fight over linking. The result is jank that iOS and Android HIG reviewers catch, and that users describe as "the app feels weird."
Why this severity: High because a broken hierarchy corrupts back-button semantics and tab-state preservation across the entire app.
mobile-navigation-linking.navigation-structure.stack-hierarchySee full patternWithout root-level state tracking, analytics firing on screen changes, auth-gated redirects, and deep-link-driven UI toggles all become per-screen boilerplate that drifts out of sync. Missing `onStateChange` means you cannot detect when a user lands on a sensitive screen to refresh a token, or log funnel events centrally. The app ends up with duplicated tracking code and screens that never tell the rest of the app where the user is.
Why this severity: Medium because the app still navigates, but cross-screen analytics and global side effects become unreliable or duplicated.
mobile-navigation-linking.navigation-structure.state-mgmtSee full patternWithout a `linking` config, every marketing email, push notification, universal link, and share-sheet URL dumps the user on Home with no context — breaking attribution, re-engagement, and the entire growth loop. This violates the App Store Readiness expectation that `app.json` scheme declarations route to real screens, and it silently kills conversion from paid acquisition channels because the destination screen the ad promised never loads.
Why this severity: Critical because every external acquisition channel and notification loses its destination when deep links are not defined.
mobile-navigation-linking.deep-linking.deep-links-definedSee full patternA deep-link config that points to a screen name that does not exist, or a pattern parameter (`:id`) that the screen reads under a different key (`route.params.productId`), causes React Navigation to resolve the link to nothing, pass `undefined` to the screen, or render with a silently missing id. The user taps a product link and lands on a blank detail page; telemetry shows a navigation success but the screen queries `undefined` and renders an error state.
Why this severity: Critical because pattern mismatches turn every deep link into a silent failure that never throws.
mobile-navigation-linking.deep-linking.pattern-mappingSee full patternUnvalidated deep link parameters are the mobile equivalent of SQL injection — an attacker crafts a URL like `myapp://product?id=../../admin` and your app executes database queries or navigation logic against that raw string. OWASP A03 (Injection) and CWE-20 (Improper Input Validation) both target exactly this pattern. A product ID that arrives as `"../config"` and lands directly in a fetch call exposes data that the user was never meant to see. Beyond security, invalid parameters crash screens and generate unhandled exceptions that are reported to production crash tracking.
Why this severity: Critical because unvalidated deep link parameters are user-controlled input that flows directly into data-fetching and navigation logic, enabling injection and unauthorized data access.
mobile-navigation-linking.deep-linking.param-validationSee full patternWithout a valid `apple-app-site-association` file, iOS treats your HTTPS links as ordinary web URLs and opens them in Safari instead of launching your app. Users who tap a password-reset link, a share URL, or a post-purchase confirmation get dropped into a browser session with no app context — they abandon, or worse, complete a flow in an unsupported UI path. Apple's Universal Links spec (apple-universal-links) requires server-side verification; if `/.well-known/apple-app-site-association` is missing or misconfigured, the OS silently falls back to Safari with no error shown to the user or the developer.
Why this severity: High because misconfigured Universal Links silently break every HTTPS deep link on iOS, routing users to a browser instead of the app and degrading retention, re-engagement, and post-purchase flows.
mobile-navigation-linking.deep-linking.universal-links-iosSee full patternWithout `assetlinks.json` published at `/.well-known/assetlinks.json` on your domain, Android cannot cryptographically verify that your app owns the domain, so the OS falls back to showing an app-chooser dialog (or opens the browser) every time a user taps an HTTPS link. This breaks email confirmation flows, referral links, and payment redirects — all of which depend on landing users directly in the app. The Android App Links spec (android-app-links) mandates both the JSON file and intent filter configuration; missing either half means app link verification fails silently in production.
Why this severity: High because missing assetlinks.json causes Android to skip app-link verification entirely, routing users to a browser or app-chooser on every HTTPS deep link instead of directly into the app.
mobile-navigation-linking.deep-linking.app-links-androidSee full patternBroken 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.
Why this severity: 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.
mobile-navigation-linking.navigation-ux.back-button-behaviorSee full patternMixing modal screens into the regular stack corrupts back-button semantics: dismissing what feels like a modal pops the user out of the flow they were mid-task in, or the modal sticks around as a permanent screen in history. Users expect modals to float above the app and close independently; anything else reads as a bug and spikes task-abandonment rates on forms and confirmations. iOS Human Interface Guidelines explicitly treat this as a presentation contract.
Why this severity: Medium because modals still display but back-button semantics and presentation animation feel broken to users.
mobile-navigation-linking.navigation-ux.modal-overlaySee full patternWhen 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.
Why this severity: Medium because one of the two deep-link paths works, but half of incoming links fail depending on app state.
mobile-navigation-linking.navigation-ux.cold-warm-deeplinksSee full patternWhen a user presses back after completing login or onboarding and returns to the splash screen, they reach a pre-auth state while already authenticated — a CWE-862 (Missing Authorization Check) scenario where the auth gate is bypassed by navigating backwards through the stack. Beyond security, splash-screen return is a confusing dead end: users see a loading screen with no active content, often conclude the app has crashed, and close it. At minimum, you lose engagement; in apps with payment or subscription flows, back-navigation to the splash can expose a path to bypass gating.
Why this severity: Medium because post-auth back-navigation to splash screens bypasses auth gating (CWE-862) and presents a UX dead-end that causes user abandonment, though exploitation requires physical device access.
mobile-navigation-linking.navigation-ux.prevent-initial-backSee full patternWhen all tabs share one stack, navigating deep into Home and then switching to Settings rewinds or contaminates Home's history — users lose the screen they were on, and tab icons stop feeling like separate surfaces. This breaks the tab metaphor that iOS and Android users expect, tanks session depth in product analytics, and forces users to re-navigate every time they glance at another tab. It is one of the most visible mobile-UX defects.
Why this severity: Medium because navigation still works but tab state is lost on every tab switch, degrading user trust.
mobile-navigation-linking.navigation-ux.tab-stacks-independentSee full patternCalling `navigation.reset()` or `popToTop()` outside of logout, onboarding, or deep-link landing flows silently erases the user's breadcrumb trail. The back button then jumps past screens the user expects to return to, breaking the fundamental mobile contract that back equals undo. Users blame the app for "losing their place," and funnels that depend on multi-step flows (checkout, signup) report inflated drop-off because the stack is being rebuilt underneath them.
Why this severity: Low because history corruption is usually recoverable and impacts navigation feel rather than core functionality.
mobile-navigation-linking.navigation-ux.navigation-historySee full patternRenamed or retired screens leave a long tail of deep links in the wild — old emails, shared URLs, indexed Google results, push-notification payloads from older app versions. Without aliases or a fallback handler, those links either crash the app or dump users on an unhandled route, breaking referral traffic and surfacing as negative store reviews. Reference-integrity on deep links is the mobile equivalent of HTTP 301 redirects on the web.
Why this severity: Low because most users use current links, but long-tail traffic and indexed URLs degrade silently without redirects.
mobile-navigation-linking.navigation-ux.deprecated-route-redirectsSee full patternNavigation jank is the first thing users notice and the last thing developers test. When a destination screen starts rendering a 500-row FlatList or firing a data fetch during the transition animation, the JS thread is saturated and the animation drops below 60 FPS. ISO 25010:2011 performance-efficiency covers exactly this: degraded responsiveness under normal operating conditions. Users experiencing jank in the first 300ms of a screen transition — when the UI is most visually exposed — rate apps lower in store reviews and uninstall at higher rates than users who experience equivalent latency hidden behind a loading state.
Why this severity: Low because jank degrades perceived quality and store ratings but does not block functionality or expose data; it is an experience failure, not a functional or security failure.
mobile-navigation-linking.navigation-ux.smooth-transitionsSee full patternMobile OSes aggressively kill backgrounded apps to reclaim memory. Without state persistence, a user who navigated three screens deep, switched to Maps for directions, and came back lands on a cold splash screen with their place lost — breaking the session and dumping any in-progress form input. This is especially punishing on iOS where low-memory terminations are silent, and it corrupts any analytics funnel that assumes continuous sessions.
Why this severity: Medium because state loss is only visible after background kills but severely degrades the session-continuity experience.
mobile-navigation-linking.state-management.persist-on-backgroundSee full patternRoute parameters that carry non-serializable values — functions, class instances, `Date` objects, circular references — silently corrupt navigation state when React Navigation serializes the stack for deep linking, state persistence, or web URL synchronization. On top of serialization breakage, passing unsanitized user input directly as route parameters (from a text input or deep link) is an OWASP A03 / CWE-20 injection surface: a crafted parameter value can overwrite screen state or trigger unexpected navigation behavior. The Mobile Offline & Storage audit (`mobile-offline-storage`) documents the parallel risk in storage serialization — the same class of defect.
Why this severity: Medium because non-serializable params break navigation state persistence and deep linking, while unsanitized user-sourced params introduce an injection vector per CWE-20 and OWASP A03.
mobile-navigation-linking.state-management.param-serializationSee full patternNavigation listeners added via `navigation.addListener()` that are never unsubscribed accumulate across screen mounts. Each time the screen re-enters the stack — through forward navigation, tab switches, or deep links — another listener stacks on top of the previous ones. The result is duplicate event firings: a `focus` handler that fires once on first mount fires twice after the second mount, three times after the third. This causes duplicate API calls, doubled analytics events, and in apps with optimistic state updates, race conditions that corrupt UI state. CWE-401 (Memory Leak) and ISO 25010:2011 reliability both cover this class of resource leak.
Why this severity: Low because listener leaks cause duplicate event firing and memory accumulation rather than data exposure or crashes, but they compound with every navigation cycle and degrade reliability over a session.
mobile-navigation-linking.state-management.listener-cleanupSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open Mobile Navigation & Deep Linking Audit