Appearance
Deep Linking Setup
Complete guide to setting up and verifying deep links with Linkrunner for React Native and Flutter apps
Deep links allow users to navigate directly to specific content within your app by clicking on a URL. This guide walks you through the complete setup — from creating your verification config files and saving them in Linkrunner, to making the necessary code changes in your app.
There are two primary approaches to deep linking:
- HTTP/HTTPS Deep Links: URLs with
http://orhttps://protocols that can open your app when clicked. Requires domain verification. - Custom URI Schemes: URLs with a custom protocol like
myapp://that are registered to your app. No verification needed.
HTTP/HTTPS Deep Linking
HTTP/HTTPS deep links (including App Links on Android and Universal Links on iOS) require you to prove domain ownership before they work reliably. The setup has four parts:
- Create your verification config files
- Save them in Linkrunner
- Update native configuration (Android & iOS)
- Configure your app's navigation
Step 1: Create Verification Config Files
Create the Digital Asset Links file
Create a file named assetlinks.json with the following content:
json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "your.package.name",
"sha256_cert_fingerprints": ["SHA-256:XX:XX:XX:..."]
}
}
]Replace:
your.package.namewith your actual Android package nameSHA-256:XX:XX:XX:...with your app's SHA-256 fingerprint
Get your app's SHA-256 fingerprint
For debug builds:
bash
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass androidFor release builds:
bash
keytool -list -v -keystore your_release_keystore.keystore -alias your_key_aliasLook for the "SHA-256 Certificate fingerprint" line in the output.
Debug and release builds are signed with different keystores, so they produce different SHA-256 fingerprints. App Links verification will only succeed for the build whose fingerprint is currently saved in Linkrunner.
Before testing or shipping a release build, either:
- Update the
sha256_cert_fingerprintsvalue in Linkrunner to your release keystore's fingerprint, or - List both fingerprints in the array so debug and release builds both verify:
json
"sha256_cert_fingerprints": [
"SHA-256:DEBUG:FINGERPRINT:...",
"SHA-256:RELEASE:FINGERPRINT:..."
]If you use Google Play App Signing, get the release fingerprint from Play Console → Setup → App integrity, not your local upload keystore.
Step 2: Save Verification Config in Linkrunner
Linkrunner hosts your verification files automatically so you don't have to manage server configuration yourself.
- Log in to your Linkrunner dashboard
- Go to Project Settings from the navigation menu
- In the Domain Verification section:
- Paste your
apple-app-site-associationJSON in the iOS (Only JSON allowed) text area - Paste your
assetlinks.jsoncontent in the Android (Only JSON allowed) text area
- Paste your
- Click Save
Linkrunner will automatically host these files at:
- iOS:
https://your-domain.io/.well-known/apple-app-site-association - Android:
https://your-domain.io/.well-known/assetlinks.json
Step 3: Update Native Configuration
These changes are the same whether you're using React Native or Flutter.
Open android/app/src/main/AndroidManifest.xml and add the following inside the <activity> section:
xml
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Your domain and subdomains -->
<data android:scheme="https" android:host="example.com" />
<data android:scheme="https" android:host="app.example.com" />
<data android:scheme="https" android:host="store.example.com" />
</intent-filter>Step 4: Configure Navigation
React Native uses React Navigation for handling deep links.
javascript
// App.js or your navigation configuration file
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import LinkRunner from "@linkrunner/react-native";
const Stack = createStackNavigator();
function App() {
const linking = {
prefixes: [
"https://example.com",
"https://app.example.com",
"https://store.example.com",
],
config: {
screens: {
Home: "",
Profile: "profile/:id",
Store: {
path: "store/:category?",
parse: {
category: (category) => category || "all",
},
},
"app.example.com": {
screens: {
AppSpecificScreen: ":id",
},
},
"store.example.com": {
screens: {
StoreSpecificScreen: ":id",
},
},
},
},
};
return (
<NavigationContainer linking={linking}>
<Stack.Navigator>{/* Your screens */}</Stack.Navigator>
</NavigationContainer>
);
}
export default App;Custom URI Schemes
Custom URI schemes use a custom protocol like myapp:// and don't require domain verification. They're useful for backward compatibility or simpler setups.
Native Configuration
Open android/app/src/main/AndroidManifest.xml and add inside the <activity> section:
xml
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>Testing Your Deep Links
Use adb to test:
bash
# Test HTTP/HTTPS deep links
adb shell am start -a android.intent.action.VIEW -d "https://app.example.com/profile/123" your.package.name
# Test custom URI scheme
adb shell am start -a android.intent.action.VIEW -d "myapp://profile/123" your.package.nameTroubleshooting
- App doesn't open automatically: Ensure your
assetlinks.jsonis correctly formatted, the SHA-256 fingerprint matches your signing key, and the file is accessible via HTTPS. - Verification warnings: Look for "IntentFilterIntentSvc" messages in logcat for details on verification failures.