What This Guide Covers
Firebase Dynamic Links is gone. If you're migrating your React Native app to HopLinks — or starting fresh — this guide walks you through the complete integration. We cover both the React Native CLI path and the Expo path (Expo SDK 54+), including deferred deep linking for new installs.
React Native CLI project or Expo SDK 54+ project · React Navigation v6+ · A free HopLinks account · Android Studio / Xcode for native builds · ADB installed for Android testing
How HopLinks Deep Linking Works
HopLinks works entirely through native OS protocols — Universal Links (AASA) on iOS and Android App Links (Digital Asset Links) on Android. There is no proprietary SDK to install in your app. HopLinks automatically hosts the verification files on your behalf based on what you enter in the dashboard.
The deferred deep linking flow works in four stages:
- Click — user taps a HopLink on any platform.
- Install — if the app isn't installed, they're redirected to the App Store or Google Play.
- Match — on first app launch, HopLinks identifies the user via device fingerprinting and matches them to the original click.
- Route — the app opens directly to the intended destination, not the generic home screen.
"Standard deep links fail if the app isn't installed. Deferred Deep Links 'remember' the destination during the installation process, ensuring the user lands exactly where they intended after their first launch."
— HopLinks Developer Guide
Dashboard Configuration
Before touching any code, register your app in the HopLinks dashboard. This is what activates the AASA and assetlinks.json endpoints that iOS and Android will verify.
- Log into your HopLinks console and go to App Settings.
- Create a new app configuration.
- For Android: enter your Package Name and SHA-256 fingerprint.
- For iOS: enter your Apple Team ID and Bundle Identifier.
Once saved, HopLinks automatically generates and serves the following files — you don't host anything yourself:
/.well-known/assetlinks.json— for Android App Link verification/.well-known/apple-app-site-association— for iOS Universal Link verification
Native Platform Setup
Both iOS and Android need to know which domain your app is authorized to handle. The HopLinks domain to use is www.hpln.in (or your custom domain if configured).
Android — AndroidManifest.xml
Add the following intent filter to your main activity in AndroidManifest.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" /> <data android:scheme="https" android:host="www.hpln.in" /> </intent-filter>
android:autoVerify="true" attribute is critical. Without it, Android will not fetch your assetlinks.json file, and deep links will silently fall back to opening in the browser instead of the app.
iOS — Xcode Associated Domains
In Xcode, navigate to your target → Signing & Capabilities → add the Associated Domains capability. Then add:
applinks:www.hpln.in
apple-app-site-association file at app install time. If your Team ID or Bundle ID in the HopLinks dashboard doesn't exactly match your Xcode settings, Universal Links will silently fail.
React Native CLI Integration
If you're using the React Native CLI (not Expo), the built-in Linking module handles incoming deep links. No extra package needed.
import React, { useEffect, useState } from 'react'; import { Linking, Text, View } from 'react-native'; const App = () => { const [initialUrl, setInitialUrl] = useState(null); useEffect(() => { // 1. App opened from cold start (was not running) const getUrlAsync = async () => { const url = await Linking.getInitialURL(); if (url) handleDeepLink(url); }; getUrlAsync(); // 2. App was already open in background const subscription = Linking.addEventListener('url', (event) => { handleDeepLink(event.url); }); return () => { subscription.remove(); }; }, []); const handleDeepLink = (url) => { setInitialUrl(url); if (url.includes('/product/')) { const id = url.split('/').pop(); console.log('Navigate to product ID:', id); // navigate('Explore', { id }) } }; return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text>Opened from: {initialUrl}</Text> </View> ); }; export default App;
Expo Integration (8 Steps)
Expo requires a few more configuration steps because the native build is managed by Expo's toolchain. Follow all 8 steps in order.
Step 4.1 — Install expo-linking
Always use npx expo install — not plain npm install — so Expo selects the version that matches your SDK.
npx expo install expo-linking
Step 4.2 — Configure app.json
Open app.json and add URL scheme, iOS associated domains, and Android intent filters:
{
"expo": {
"scheme": "myapp",
"ios": {
"bundleIdentifier": "com.example.myapp",
"associatedDomains": [
"applinks:www.hpln.in",
"applinks:hoplinks.in"
]
},
"android": {
"package": "com.example.myapp",
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{ "scheme": "https", "host": "www.hpln.in" },
{ "scheme": "https", "host": "hoplinks.in" }
]
}
]
}
}
}
scheme or intentFilters. These changes are not hot-reloaded.
Step 4.3 — Configure React Navigation
Create a linking object that maps URL paths to screens, and pass it to NavigationContainer:
import * as Linking from 'expo-linking'; import { NavigationContainer } from '@react-navigation/native'; // Base prefix for Expo Go development URLs const prefix = Linking.createURL('/'); const linking = { prefixes: [ prefix, 'myapp://', 'https://www.hpln.in', 'https://hoplinks.in', ], config: { screens: { Home: 'home', Explore: 'product/:id', // :id is a dynamic param Notifications: 'notifications', Profile: 'profile', }, }, }; export default function App() { return ( <NavigationContainer linking={linking}> {/* Your navigator here */} </NavigationContainer> ); }
URL to Screen Mapping Reference
| Screen | Path Pattern | Example URL |
|---|---|---|
| Home | home | myapp://home |
| Explore | product/:id | myapp://product/id123 |
| Notifications | notifications | myapp://notifications |
| Profile | profile | myapp://profile |
Step 4.4 — Read Parameters in Screens
React Navigation automatically injects URL segments as route params. Use the useRoute hook to access them:
import { useRoute } from '@react-navigation/native'; import { View, Text } from 'react-native'; export default function ExploreScreen() { const route = useRoute(); const deepLinkId = route.params?.id; // from :id in the URL pattern return ( <View> {deepLinkId && ( <Text>You opened product: {deepLinkId}</Text> )} </View> ); }
Linking.getInitialURL() manually when using React Navigation's linking config. React Navigation handles the initial URL automatically.
Step 4.5 — Domain Verification (Automatic)
This step requires no code. HopLinks automatically generates and serves both verification files based on your App Settings in the dashboard. Simply ensure your Package Name, SHA-256 fingerprint (Android), Team ID, and Bundle ID (iOS) are correctly entered.
- Android:
/.well-known/assetlinks.json— served automatically - iOS:
/.well-known/apple-app-site-association— served automatically
Step 4.6 — Testing the Integration
Testing approach depends on whether you're running Expo Go or a native build.
Testing in Expo Go — use ADB
adb shell am start -W -a android.intent.action.VIEW \
-d "exp://127.0.0.1:8081/--/product/id123"
https:// Universal Links inside Expo Go. The OS blocks them because Expo Go's certificate does not match your assetlinks.json. Use the Expo Go test URL format above.
Testing Native Builds — use uri-scheme CLI
# Test the custom URI scheme first npx uri-scheme open "myapp://product/id123" --android # Once scheme works, test verified App Link npx uri-scheme open "https://hoplinks.in/product/id123" --android
myapp:// custom scheme routing before testing https:// App Links. This isolates navigation issues from domain verification issues.
Step 4.7 — Configure HopLinks Dashboard Links
Each HopLinks short link needs an In-App Deep Link value that maps to a screen in your app. In the dashboard, after setting the standard destination URL (web fallback), scroll to the "Deep Link Intent" section and set the value:
| Target Screen | In-App Deep Link value | What it opens |
|---|---|---|
| Home Screen | myapp://home | Opens the Home tab |
| Explore (product) | myapp://product/promo-123 | Opens Explore with id=promo-123 |
| Notifications | myapp://notifications | Opens Notifications screen |
| Profile Screen | myapp://profile | Opens the Profile screen |
How HopLinks uses these values at runtime:
- App installed: the phone intercepts the link and React Navigation routes to the correct screen.
- App not installed: the custom scheme fails silently; HopLinks redirects the user to the App Store or Google Play.
Step 4.8 — Handle Deferred Deep Linking
This is the critical step for preserving intent across installs. Without it, users who click a HopLink, install the app, and open it for the first time will land on the generic Home screen — not the content they originally clicked.
Add a getInitialURL() function to your linking config:
const linking = { prefixes: [prefix, 'myapp://', 'https://www.hpln.in', 'https://hoplinks.in'], // This catches the URL that originally launched the app async getInitialURL() { // Check if opened via a standard OS deep link const url = await Linking.getInitialURL(); if (url != null) return url; // Optional: call HopLinks API for deferred link data // const deferredUrl = await Hoplinks.getDeferredLink(); // if (deferredUrl) return deferredUrl; return null; }, config: { screens: { Home: 'home', Explore: 'product/:id', Notifications: 'notifications', Profile: 'profile', }, }, };
To verify the complete deferred flow: click a HopLinks URL on a device that does not have your app installed → install from the store → confirm the app opens the correct screen on first launch.
Quick Reference — Do's & Don'ts
✔ Do
- Ensure
assetlinks.jsonpackage_name exactly matchesapp.json - Restart the Expo dev server after changing scheme or intentFilters
- Use human-readable URL paths (e.g.
hoplinks.in/explore/design) - Test
myapp://scheme routing before testinghttps://links - Test on real physical devices for final verification
✘ Don't
- Test
https://links in Expo Go — the OS blocks them - Add a
.jsonextension to the apple-app-site-association file - Forget to update SHA-256 fingerprint when switching to production builds
- Serve
assetlinks.jsonwith HTML content-type - Paste Universal Link URLs into the browser address bar — they won't trigger
Troubleshooting
Android — Links opening in browser instead of the app
- Confirm
android:autoVerify="true"is present in the intent filter. - Android can take up to 20 seconds after install to fetch and verify
assetlinks.json. Try uninstalling and reinstalling the app before retesting. - Check that the SHA-256 fingerprint in the HopLinks dashboard matches your current build (debug and release fingerprints are different).
iOS — Universal Links not triggering
- Confirm your Apple Team ID and Bundle ID in the HopLinks dashboard exactly match your Xcode settings — even a single character difference breaks verification.
- Universal Links only trigger from a "click" in an external app (Notes, Mail, WhatsApp). Pasting the URL into Safari's address bar will not trigger them.
- iOS fetches the AASA file at app install time, not at runtime. Reinstall the app after any changes.
Expo Go — https:// links not working
This is expected behavior. Expo Go cannot verify your AASA or assetlinks.json because its certificate doesn't match your app. Switch to a development build (npx expo run:android or npx expo run:ios) to test https:// links.
A complete working Expo demo project is available on GitHub: github.com/monkastro/Hoplinks_Codelab/tree/main/ExpoDemo. Clone and run it to see the full integration in action before applying it to your own project.
Frequently Asked Questions
Do I need to install any SDK or native module in my React Native app?
No. HopLinks works entirely through native OS protocols — Universal Links on iOS and Android App Links. The only package required for Expo is expo-linking, which is already part of the Expo ecosystem. For React Native CLI, the built-in Linking module is sufficient. No third-party SDK is added to your app binary.
Does HopLinks support Expo Go for testing?
Partially. You can test custom URI scheme links (myapp://) in Expo Go using ADB. However, you cannot test https:// Universal Links or App Links in Expo Go because the OS blocks them — Expo Go's certificate won't match your assetlinks.json. For https:// testing, use a development build via npx expo run:android or npx expo run:ios.
Do I need to host the AASA or assetlinks.json files myself?
No. HopLinks automatically generates and hosts both files based on the app details you enter in the dashboard (Package Name, SHA-256 fingerprint, Team ID, Bundle ID). You don't set up or manage any server-side files.
What happens if the user doesn't have the app installed when they click a HopLink?
HopLinks redirects the user to the App Store (iOS) or Google Play (Android). If you have deferred deep linking configured via getInitialURL() in your linking config, the user will be routed to the correct in-app destination on first launch after install. Without this step, they'll land on your app's default home screen.
My Android deep links are opening in the browser instead of the app. What's wrong?
The most common causes are: (1) android:autoVerify="true" is missing from the intent filter, (2) the SHA-256 fingerprint in the HopLinks dashboard doesn't match your current build — debug and release fingerprints are different, or (3) Android hasn't finished verifying the assetlinks.json yet — it can take up to 20 seconds after install. Uninstall and reinstall the app and wait before testing again.
Does this work with React Navigation v6?
Yes. The linking config pattern shown in this guide is specifically designed for React Navigation v6's NavigationContainer. The getInitialURL() function and config.screens object are both standard React Navigation v6 API.
Ready to Set Up HopLinks?
Create your free account, configure your app, and have your first deep link working in under 5 minutes. No credit card. No SDK bloat.
Create Free Account → Full Docs →