TechnicalApril 2026 · 12 min read

Expo Deep Linking Setup: The Complete 2026 Walkthrough

Deep linking is the lever that turns email campaigns, push notifications, Instagram bio links, and QR codes into app opens at the right screen. This guide walks through setting up custom schemes, iOS Universal Links, and Android App Links in an Expo project in 2026 — end to end.

Quick path

Add a scheme to app.json, configure associatedDomains and intentFilters, host two JSON files at /.well-known/ on your domain, test on a real device. 1 hour on a clean Expo project.

Custom schemes vs Universal/App Links

AspectCustom schemeUniversal / App Links
URL formatmyapp://pathhttps://yourapp.com/path
Works uninstalledNo — does nothingYes — falls back to web
Browser behaviorPrompts userOpens app silently if installed
Setup complexityLow (app.json only)Higher (domain verification)
Best forInternal, dev, push payloadsMarketing, email, social

Production apps should set up both: custom scheme for internal use, Universal/App Links for all external marketing.

Step 1: custom scheme in app.json

// app.json
{
  "expo": {
    "scheme": "myapp",
    "ios": {
      "bundleIdentifier": "com.yourco.myapp"
    },
    "android": {
      "package": "com.yourco.myapp"
    }
  }
}

After rebuild, myapp://profile/123 opens your app. Expo Router handles the routing from the URL to the matching file-based route automatically.

Step 2: iOS Universal Links

Configure associatedDomains in app.json:

// app.json
{
  "expo": {
    "ios": {
      "associatedDomains": [
        "applinks:yourapp.com"
      ]
    }
  }
}

Host apple-app-site-association at https://yourapp.com/.well-known/apple-app-site-association (no extension, served as application/json):

{
  "applinks": {
    "apps": [],
    "details": [{
      "appID": "TEAMID.com.yourco.myapp",
      "paths": ["*"]
    }]
  }
}

Replace TEAMID with your Apple Team ID (found in the Apple Developer portal membership page).

Step 3: Android App Links

Configure intentFilters:

// app.json
{
  "expo": {
    "android": {
      "intentFilters": [{
        "action": "VIEW",
        "autoVerify": true,
        "data": [{
          "scheme": "https",
          "host": "yourapp.com"
        }],
        "category": ["BROWSABLE", "DEFAULT"]
      }]
    }
  }
}

Host assetlinks.json at https://yourapp.com/.well-known/assetlinks.json:

[{
  "relation": ["delegate_permission/common.handle_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.yourco.myapp",
    "sha256_cert_fingerprints": [
      "XX:XX:XX:..."
    ]
  }
}]

The SHA-256 fingerprint comes from your EAS Build signing cert. Run eas credentials on Android to retrieve it.

Step 4: handle links in code

With Expo Router, this is usually done for you — a link like https://yourapp.com/profile/42 routes to app/profile/[id].tsx automatically. For edge cases, read the incoming URL manually:

import * as Linking from 'expo-linking';
import { useEffect } from 'react';

useEffect(() => {
  Linking.getInitialURL().then((url) => {
    if (url) handleInitialLink(url);
  });
  const sub = Linking.addEventListener('url',
    ({ url }) => handleIncomingLink(url));
  return () => sub.remove();
}, []);

Testing

  • Custom scheme in dev: npx uri-scheme open myapp://profile/42 --ios.
  • iOS Universal Links: send yourself an iMessage or email with the https link — tapping it should open the app. Terminal curl of the AASA file must return valid JSON.
  • Android App Links verification: adb shell pm verify-app-links --re-verify com.yourco.myapp.
  • Apple’s debugger: Settings → Developer → Universal Links on iOS to inspect link matching.

Common pitfalls

  • apple-app-site-association served with wrong MIME type or behind a redirect. Must be 200 + valid JSON.
  • Missing Team ID or wrong bundle ID in the AASA file. Apple won’t tell you — links just silently fall back to the browser.
  • Android SHA-256 cert mismatch after switching between EAS build profiles. Keep prod cert fingerprints in assetlinks.json.
  • Testing Universal Links in the simulator. They don’t work — use a real device.
  • Not hosting a real web page at the same URL. When the app isn’t installed, users land on a 404.

Frequently Asked Questions

What is the difference between deep links, Universal Links, and App Links?

Deep links are custom-scheme URLs (myapp://profile/123) that open your app if it's installed. Universal Links (iOS) and App Links (Android) use normal https URLs that open your app directly if installed — or fall back to a web page. Universal/App Links are the modern default; custom schemes are the fallback.

Does Expo Router set up deep linking automatically?

Yes — routes in Expo Router map to URLs by default. You still need to configure the custom scheme in app.json and host the apple-app-site-association and assetlinks.json files for iOS and Android. The routing itself is free.

Can I test deep links in Expo Go?

Custom scheme deep links work in Expo Go using the exp:// scheme. Universal Links and App Links require a production-signed build on a real device — they will not work in Expo Go or the iOS Simulator.

Where do I host apple-app-site-association and assetlinks.json?

At the root of the domain your app links point to — e.g., https://yourapp.com/.well-known/apple-app-site-association and https://yourapp.com/.well-known/assetlinks.json. They must be served over HTTPS with a valid cert and must not redirect.

How do I handle deep link hits when the app is not installed?

Universal Links / App Links fall back to the web URL, so you host a real web page at the same path. Custom-scheme links do nothing if the app is not installed. For broad linking campaigns, use Universal Links so every user lands somewhere useful.

Expo Router vs React Navigation

Why Expo Router's file-based routes make deep linking easier.

Read guide →

Push Notifications in Expo

Deep-link from notifications to specific screens.

See guide →

Ship a real React Native app today

Describe, preview, and export Expo code — free to start.

Build with ShipNative →