Documentation Index
Fetch the complete documentation index at: https://docs.linkrunner.io/llms.txt
Use this file to discover all available pages before exploring further.
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:// or https:// 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:[
{
"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.name with your actual Android package name
SHA-256:XX:XX:XX:... with your app’s SHA-256 fingerprint
Get your app’s SHA-256 fingerprint
For debug builds:keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
For release builds:keytool -list -v -keystore your_release_keystore.keystore -alias your_key_alias
Look 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_fingerprints value in Linkrunner to your release keystore’s fingerprint, or
- List both fingerprints in the array so debug and release builds both verify:
"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. Create the Apple App Site Association file
Create a file named apple-app-site-association (no file extension) with the following content:{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.BUNDLE_ID",
"paths": ["/*"]
}
]
}
}
Replace:
TEAM_ID with your Apple Developer Team ID (found in the Apple Developer Portal under Membership Details)
BUNDLE_ID with your app’s bundle identifier (found in your Xcode project settings)
The paths array can be customized to include only specific paths your app should handle. Use /* to handle all paths.
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-association JSON in the iOS (Only JSON allowed) text area
- Paste your
assetlinks.json content in the Android (Only JSON allowed) text area
- 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:<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>
- Open your iOS project in Xcode
- Go to Signing & Capabilities
- Add the Associated Domains capability
- Add your domains:
applinks:example.com
applinks:app.example.com
applinks:store.example.com
For more details, see the official Apple documentation.
React Native uses React Navigation for handling deep links.// 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;
Flutter uses go_router or its own navigation system to handle deep links.import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:linkrunner/linkrunner.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
final GoRouter _router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
path: '/profile/:id',
builder: (context, state) {
final id = state.params['id']!;
return ProfileScreen(id: id);
},
),
GoRoute(
path: '/app/:id',
builder: (context, state) {
final id = state.params['id']!;
return AppSpecificScreen(id: id);
},
),
GoRoute(
path: '/store/:category',
builder: (context, state) {
final category = state.params['category'] ?? 'all';
return StoreScreen(category: category);
},
),
],
redirect: (context, state) {
final uri = Uri.parse(state.location);
if (uri.host == 'app.example.com') {
return '/app/${uri.pathSegments.isNotEmpty ? uri.pathSegments.first : ''}';
} else if (uri.host == 'store.example.com') {
return '/store/${uri.pathSegments.isNotEmpty ? uri.pathSegments.first : 'all'}';
}
return null;
},
);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
title: 'My 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:<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>
- Open your iOS project in Xcode
- Go to the Info tab
- Add a new entry to URL Types with:
- Identifier: Your app bundle identifier (e.g.,
com.example.myapp)
- URL Schemes: Your custom scheme (e.g.,
myapp)
In Info.plist:<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.example.myapp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
Testing Your Deep Links
Use adb to test:# 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.name
Troubleshooting
- App doesn’t open automatically: Ensure your
assetlinks.json is 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.
Use the iOS Simulator:# Test HTTP/HTTPS deep links
xcrun simctl openurl booted "https://app.example.com/profile/123"
# Test custom URI scheme
xcrun simctl openurl booted "myapp://profile/123"
Troubleshooting
- App doesn’t open with Universal Links: Ensure your
apple-app-site-association file is correctly formatted, Team ID and Bundle ID are correct, the file is accessible via HTTPS, and Associated Domains capability is enabled.
- Debug Universal Links: Connect your device to a Mac, open Console.app and filter for “swcd” to see Universal Links logs.
Create actual links on your website and test on real devices:
- For HTTP/HTTPS links:
https://app.example.com/profile/123
- For custom URI schemes:
myapp://profile/123