Deep Linking
A deep link is a URL that, when tapped, doesn't just open your website. It opens your app on a specific screen.
You see deep links every day without noticing:
- Tap a link to a tweet in an email → Twitter app opens to that tweet.
- Click a link to a YouTube video on your phone → YouTube app opens to that video.
- Get a password reset email → tap the link → your app opens to "set a new password," already signed in.
Deep links power sharing, referrals, OAuth callbacks, and email CTAs. Setting them up takes a tiny bit of plumbing, and the agent walks you through it.
Two kinds of deep links
1. Custom URL scheme. The simple kind
A link like myapp://post/123. When tapped, the operating system finds the app that "owns" myapp:// and hands the link over.
Pros: dead simple, no server setup.
Downside: if the user hasn't installed your app, the link just fails. There's no graceful fallback to a website.
Use this for internal stuff: OAuth redirects, deep links inside dev builds, links between your own emails and your own app.
2. Universal links. The polished kind
A link like https://myapp.com/post/123. When tapped, if your app is installed, the OS opens your app. Otherwise, the browser loads the URL like a normal website.
Pros: graceful fallback. Users without your app get a webpage; users with your app get the app. Perfect for shareable content.
Downside: requires a little DNS and a small file hosted on your website. The agent generates the file for you; you upload it once.
Use this for any link that gets shared (a referral, an email CTA, a "share this post" button).
The setup prompt
"Set up deep linking. Use
habitforgeas the custom scheme for OAuth and dev links. Also set up universal links for habitforge.app. Sharing a post link from the app should open the post in the app if the recipient has it installed."
What the agent does:
- Registers the custom scheme
habitforge://with iOS and Android. - Wires up the app's routes so URLs map to the right screens.
- Generates the two small verification files you need to host on your website.
- Configures iOS and Android to associate your domain with your app.
The files you need to host
For universal links to work, two small files need to live at exact paths on your website. The agent generates them; you upload them once.
For iPhone
A file at: https://yourdomain.com/.well-known/apple-app-site-association
Looks like:
{
"applinks": {
"apps": [],
"details": [
{ "appID": "TEAMID.com.yourdomain.app", "paths": ["*"] }
]
}
}Must be served as JSON, over HTTPS, with no redirects.
For Android
A file at: https://yourdomain.com/.well-known/assetlinks.json
Looks like:
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.yourdomain.app",
"sha256_cert_fingerprints": ["<YOUR_CERT_FINGERPRINT>"]
}
}
]The "cert fingerprint" comes from Google's Play Console. They show it under Setup → App Integrity.
The agent fills both files in for you; you just need to host them.
Where deep links open in the app
After setup, opening myapp://post/123 (or https://yourdomain.com/post/123) navigates the app to a screen at app/post/[id].tsx, with id = "123".
The agent wires this up; you don't have to think about it. You just describe the user experience:
"When someone taps a shared post link, open the post detail screen directly. If the user isn't signed in, send them to the log-in screen first, then to the post after they sign in."
Common real-world uses
Email "call to action" buttons
Your welcome email has a big "Start your first habit" button. The URL points to:
https://habitforge.app/new-habit?from=welcome-email- User has the app → app opens to the new-habit flow.
- User doesn't have the app → website renders a "Get the app" page.
"Sign in with Google". The redirect back
When a user signs in with Google, Google sends them to a URL after authorising. That URL is a deep link back into your app: myapp://auth/callback. The agent wires this when you turn social log-in on.
Referrals
"Add a 'Share this habit' button. Generate a deep link that, when tapped, pre-fills the new-habit flow with the shared habit's details."
Password reset emails
The agent automatically wires this when you enable email + password sign-in. The reset link in the email opens the app to the right screen, already authenticated.
Testing deep links
While you're developing, you can test deep links manually:
iPhone simulator:
xcrun simctl openurl booted "myapp://post/123"Android emulator:
adb shell am start -a android.intent.action.VIEW -d "myapp://post/123"Real device: paste the link into Notes and tap it.
For universal links, after you've hosted the verification files, just tap a link to your domain on a device with your app installed. It should open in-app. If it opens in Safari instead, the verification file isn't being served correctly.
Things that go wrong
- Link previews break. Services like iMessage, Slack, and Twitter fetch your site to build a link preview. If your
apple-app-site-associationrequires authentication or returns a redirect, those previews break. Serve it cleanly. - First-time installs lose context. When a new user installs your app from a shared link, you usually want to remember which link they came from. Branch and Adjust are services that do this ("deferred deep linking"); they're out of scope for the agent by default, but it can scaffold them.
- Sub-paths. If you want
/post/*to open the app but/aboutto open the website, narrow thepathsarray in the verification file.
Sharing deep links from inside your app
When users hit "Share," React Native's built-in share sheet opens with whatever message and URL you provide:
"Add a Share button on each post. The message should be 'Check out this habit: [link to the habit]'."
The agent wires this up.
Under the hood (for engineers)
Expo Router resolves incoming URLs against the app/ directory structure. The custom scheme lands in app.json under expo.scheme. iOS uses Associated Domains (applinks:domain.com); Android uses intent filters with autoVerify="true".
Verify on a device:
- iOS: tap a link; should open in-app. If Safari opens, the verification file is wrong or unreachable.
- Android:
adb shell pm get-app-links com.yourapp.android. Look forverifiednext to each domain.
Next
Read Analytics to see what users do once they're in.
