Deep linking is one of those features that seems simple until your app has multiple route groups, authentication gates, push notifications, and different behavior on iOS, Android, and web. This guide gives you a reusable checklist for handling deep linking in React Native with Expo Router and React Navigation, including setup decisions, testing steps, and the edge cases that usually surface late in QA. If your routes change, your auth flow changes, or your distribution setup changes, this is the kind of implementation guide worth revisiting.
Overview
This article gives you a durable approach to react native deep linking instead of a one-time snippet. The goal is not only to open a screen from a URL, but to make deep links predictable across cold starts, warm starts, protected routes, and external entry points like email, QR codes, and push notifications.
At a high level, deep linking means mapping an external URL to an in-app destination. In practice, you are usually dealing with two related categories:
- Custom URL schemes, such as
myapp://profile/42, which are useful for development, app-to-app handoff, and simpler mobile-only flows. - Universal Links and App Links, such as
https://example.com/profile/42, which are generally better for user-facing links because they can open your app when installed and still resolve on the web when it is not.
If you are using Expo Router, file-based routing handles much of the path-to-screen mapping for you. If you are using React Navigation, you usually define a linking configuration manually. Both approaches can work well. The difference is mostly where the route structure lives and how much explicit configuration you need to maintain.
Use this article as a checklist before release and again whenever you change any of the following:
- route names or nested navigators
- authentication flow
- domain or URL structure
- push notification payloads
- marketing campaign links
- native app identifiers or build configuration
If your app also includes login redirects, passwordless sign-in, or magic links, pair your deep link review with your authentication flow. A related guide is How to Add Authentication to React Native: Email, OAuth, Magic Links, and Passkeys.
Checklist by scenario
This section gives you a practical checklist by implementation path and app behavior. Start with the scenario that matches your stack, then work through the cross-cutting checks.
Scenario 1: Expo Router deep link setup
If you are building with Expo Router, the main advantage is that your route segments already define much of the URL structure. That reduces manual linking work, but you still need to think through prefixes, domains, and protected navigation.
- Choose your external URL format first. Decide whether the app should primarily receive links via a custom scheme, a web domain, or both.
- Set a stable scheme. Add a scheme in your app configuration so links like
myapp://can be resolved during development and production. - Map file-based routes to user-facing paths deliberately. Just because a route exists does not mean its segment name is ideal for external links. Prefer durable, readable paths.
- Review dynamic segments. Make sure routes like
/products/[id]or/invite/[token]are resilient when params are missing, malformed, or expired. - Handle route groups carefully. Internal grouping folders are useful, but your public deep-link structure should stay simple and unsurprising.
- Test initial launch and already-open app behavior separately. Many deep-link bugs only appear on cold start.
- Plan fallback UI. If the link target is invalid, deleted, or requires permissions, show a recovery path instead of leaving users on a blank screen.
Expo Router is especially nice when your app also targets web, because the route shape can stay aligned. That said, resist the temptation to expose every internal route externally. Deep links are part of your public interface.
Scenario 2: React Navigation deep linking setup
For a react navigation deep linking setup, your linking config is the source of truth. That gives you flexibility, but it also means you need to keep route config and navigator structure synchronized.
- Define your prefixes. Include your custom scheme and any web domains the app should recognize.
- Create a linking config for nested navigators. Be explicit about stacks, tabs, and modal routes so paths resolve consistently.
- Normalize path names. Keep route keys internal if needed, but expose clean public paths like
/settings/profileor/orders/123. - Parse params defensively. Query strings and path params should be validated before use.
- Support backward compatibility where practical. If you rename a route, keep an alias or redirect strategy for older links.
- Decide which routes are linkable. Not every modal, onboarding step, or transient screen should be opened directly.
If you use tabs, nested stacks, or conditional navigators, your deep linking config deserves the same care as your API contract. A typo in one screen path can break a campaign link, a support email, or an auth callback.
Scenario 3: Protected routes and authentication
This is where many implementations fail. The incoming link is valid, but the target screen depends on user state. If the app launches into an unauthenticated session, you need a clear rule for what happens next.
- Decide whether to block, defer, or partially render. For example, should an unauthenticated user be sent to sign-in and then returned to the original destination?
- Preserve the intended destination. Store the incoming path or structured route target before redirecting to login.
- Expire sensitive links safely. Invite tokens, password reset tokens, or one-time actions should fail gracefully when no longer valid.
- Avoid redirect loops. If login itself is linked or embedded in a route group, make sure your guard logic does not bounce between screens.
- Separate navigation state from auth readiness. Wait until your auth store has loaded before deciding the final route.
This becomes even more important with magic links or OAuth callbacks. Your app is not just opening a screen; it may be completing a security-sensitive state transition.
Scenario 4: Universal Links and App Links
If your app receives public-facing URLs from email, search, or shared links, support for universal links react native and app links react native is often worth the extra setup. The implementation details vary by platform, but your planning checklist is stable.
- Use a domain you control. Public deep links should live on a domain that your product team can keep stable.
- Keep path patterns intentional. Reserve a predictable subset of routes for app-opening behavior.
- Plan web fallback pages. If the app is not installed, the URL should still provide value in a browser.
- Avoid fragile campaign URLs. If marketing links depend on short-lived query params or third-party redirects, test the full redirect chain.
- Confirm platform-specific association files and entitlements during release prep. These are easy to overlook when moving from local builds to production builds.
A good public link strategy improves more than navigation. It helps support, lifecycle messaging, and user trust because the same URL can work across app and web contexts.
Scenario 5: Push notifications, QR codes, and shared content
Many teams first encounter deep linking through notifications, but the transport is only part of the story. The same route resolution logic should power links from notifications, QR codes, help center pages, and referral programs.
- Make notification payloads reference stable routes. Avoid deeply coupling notifications to internal screen names.
- Include enough context to recover if the destination changes. A content type plus ID is often safer than a brittle navigator path.
- Validate payloads before navigation. Notifications can arrive late, after content has changed or been removed.
- Test foreground, background, and killed-app states. Each can produce different navigation timing issues.
- Use the same route builder across the app. Do not handcraft one URL for notifications, another for QR codes, and another for emails.
If you are already working on notifications, see React Native Push Notifications Guide: Expo Notifications vs Firebase vs OneSignal for a broader view of delivery and product tradeoffs.
Scenario 6: Dynamic content screens
Deep links frequently target screens like product details, user profiles, form drafts, maps, or camera workflows. These screens need stronger guardrails than a static settings page.
- Design loading and error states for direct entry. A screen opened from a deep link may not have any preloaded app state.
- Refetch content on entry when appropriate. Do not assume the list screen ran first.
- Handle deleted or inaccessible resources cleanly. Show a clear message and a route back into the app.
- Be careful with heavy screens. Direct-entry screens that use maps, camera access, or large lists need good startup behavior.
Related implementation patterns can overlap with these guides: React Native Maps Guide: Google Maps, Apple Maps, Clustering, and Performance Tips, React Native Camera Libraries Compared: Expo Camera, VisionCamera, and Native Options, and React Native FlatList and FlashList Benchmarks: When to Use Each.
What to double-check
This is the pre-release checklist that catches the issues most likely to slip through.
- Cold start behavior: The app should open to the correct screen from a fully closed state, not just when already running.
- Warm start behavior: If the app is in memory, the deep link should not create duplicate screens or reset unrelated state.
- Back navigation: After opening from a link, the back button behavior should make sense. Users should not land in a dead end.
- Auth gating: Protected routes should preserve the intended destination and return users after sign-in.
- Invalid params: Bad IDs, missing tokens, and malformed query strings should lead to safe fallback UI.
- Encoding: Names, spaces, special characters, and query params should be encoded and decoded correctly.
- Route renames: Old URLs should not silently fail after navigation refactors.
- Analytics: If you track entry points, make sure deep-link opens are recorded consistently without polluting screen metrics.
- Accessibility: Directly opened screens still need clear focus order, readable status messages, and logical screen titles. See React Native Accessibility Checklist: Screen Readers, Focus, Contrast, and Dynamic Type.
- Theme readiness: If a deep-linked screen opens in dark mode, modal overlays, loading states, and status bar styling should still be correct. See How to Build Dark Mode in React Native: Themes, System Sync, and Design Tokens.
- Testing coverage: Add link-opening scenarios to integration or end-to-end tests where possible. A useful companion is React Native Testing Strategy: Unit, Integration, and E2E Tools Compared.
One practical tip: keep a small table in your repo that lists every supported deep link, its intended screen, required auth state, expected params, and fallback behavior. This becomes much easier to maintain than rediscovering behavior from scattered code.
Common mistakes
These are the deep-linking mistakes that repeatedly cause regressions.
- Treating deep linking as navigation only. It is also an entry-state problem involving auth, data loading, and platform integration.
- Using internal screen names as public URLs. Public paths should be stable even if your navigator structure changes.
- Forgetting nested navigator behavior. Links often break when tabs, stacks, and modals are composed without a clear linking model.
- Not testing real devices and production-like builds. A local simulator check is useful, but it does not catch every platform association or install-state issue.
- Dropping the intended destination after login. Users tap a link to reach something specific. Sending them to the home screen after auth is a poor fallback.
- Assuming the screen already has state. Deep-linked screens must work as first entry points.
- Ignoring stale links. Content changes, products are removed, invites expire, and routes are renamed. You need humane failure states.
- Creating multiple URL builders. Centralize path generation so links from marketing, notifications, and in-app sharing stay aligned.
- Mixing route responsibility with component logic. Keep route parsing, validation, and redirect decisions close to your navigation layer rather than spread across unrelated screens.
Another subtle mistake is failing to revisit links when forms or state models change. If a link opens a prefilled form, review your validation and payload assumptions. For broader form architecture patterns, see React Native Forms Compared: React Hook Form, Formik, and Zod Validation Patterns. If route state is also tied into your global store, revisit how navigation data interacts with your state model in Best React Native State Management in 2026: Redux Toolkit, Zustand, Jotai, and Context.
When to revisit
Deep linking is not a one-and-done task. Revisit it before release cycles and whenever workflows or tools change. A short recurring review can prevent broken links from reaching users.
Use this action list whenever any of the following happens:
- You add or rename routes. Check whether old paths still need support and whether public URLs remain sensible.
- You change your auth flow. Re-test sign-in redirects, token-based actions, and protected destinations.
- You adopt Expo Router or restructure React Navigation. Route mapping and nested navigator assumptions may need a full review.
- You add new notification or email campaigns. Confirm every user-facing link resolves correctly from a fresh install and a returning session.
- You update your domain strategy. Public app URLs, fallback pages, and link ownership should stay coherent.
- You add native integrations. Camera, maps, file sharing, and passkey flows can introduce new entry points and permission states.
- You expand testing or CI. Add stable link-opening cases to prevent regressions over time.
A practical maintenance routine looks like this:
- List your supported deep links and who owns them.
- Verify which are public, private, or auth-protected.
- Test cold start, warm start, and background resume.
- Check fallback behavior for invalid content and expired tokens.
- Confirm analytics, accessibility, and visual polish on direct entry.
- Update docs in the repo so future route changes do not silently break links.
If you only take one thing from this guide, make it this: deep links are part of your app's external contract. Treat them with the same care you give API endpoints, auth callbacks, and notification payloads. Whether you are building an expo router deep link flow or a more manual react navigation deep linking setup, the durable solution is a clear route model, consistent testing, and a documented checklist you revisit whenever your app changes.