How to Set Up Deep Links in Expo

Universal links for iOS and App Links for Android in Expo. The `app.json` config, the Apple AASA file, and how to test both before you ship.

Learn/How to Set Up Deep Links in Expo
intermediate2 hours

How to Set Up Deep Links in Expo

Universal links for iOS and App Links for Android in Expo, with the AASA and assetlinks config I use across shipped apps.

Prerequisites

Expo projectOwned domainTeam ID

What you ship at the end

Open https://yourapp.com/share/abc123 on a phone with your app installed and it opens your app directly on the share screen. Open it without your app installed and it falls back to a web page you control.

Prerequisites

  • Expo project with expo-linking and Expo Router.
  • A domain you own.
  • An https endpoint that can serve the apple-app-site-association file and the assetlinks.json file.

Step 1: Configure app.json

Add an associatedDomains entry for iOS and intentFilters for Android:

{
  "expo": {
    "scheme": "aiappfactory",
    "ios": {
      "associatedDomains": ["applinks:yourapp.com"]
    },
    "android": {
      "intentFilters": [
        {
          "action": "VIEW",
          "data": [{ "scheme": "https", "host": "yourapp.com" }],
          "category": ["BROWSABLE", "DEFAULT"],
          "autoVerify": true
        }
      ]
    }
  }
}

Step 2: Host the AASA file

Apple looks for https://yourapp.com/.well-known/apple-app-site-association. No redirects. No trailing whitespace. application/json content type.

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAMID.com.yourcompany.yourapp",
        "paths": ["/share/*", "/invite/*"]
      }
    ]
  }
}

Step 3: Host assetlinks.json for Android

At https://yourapp.com/.well-known/assetlinks.json:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourcompany.yourapp",
      "sha256_cert_fingerprints": ["YOUR:SHA:256:FINGERPRINT"]
    }
  }
]

Get the fingerprint from eas credentials or your upload keystore.

Step 4: Handle the link in-app

With Expo Router, a file at app/share/[id].tsx handles the route. useLocalSearchParams() gives you the id. Old style: Linking.addEventListener(''url'').

Step 5: Test before you ship

  • iOS: Use the Apple validator at https://branch.io/resources/aasa-validator/ to sanity-check the AASA file. Then test with xcrun simctl openurl booted https://yourapp.com/share/abc.
  • Android: adb shell am start -a android.intent.action.VIEW -d "https://yourapp.com/share/abc".

The one thing that always trips me

The AASA file is cached by Apple. If you upload a new one, expect ~24 hours before iOS picks it up. Do not panic when it does not work immediately after the upload.

What the boilerplate gives you

AI App Factory ships app.json with the deep link config, the AASA and assetlinks templates, and a /share/[id] Expo Router screen as a reference. You swap the domain and ship.

See pricing

Or let AI App Factory handle this for you.

Everything in this guide is already pre-configured in AI App Factory. 11 AI agents automate the rest.

AI App FactoryLearnHow to Set Up Deep Links in Expo