React Native Linking: A Complete Guide for 2026
A user taps a campaign link for a specific product, offer, or shared piece of content. Your app opens. Then it lands on the home screen.
That single failure is what usually sends teams searching for react native linking help. The app technically opened, so it looks half-correct in a demo. In production, though, it breaks context, hurts onboarding, and makes every marketing link feel unreliable.
Teams usually discover the problem from three directions at once. Product wants direct entry into a screen. Marketing wants campaign attribution. Engineering wants one routing model that doesn't turn into platform-specific glue code. React Native gives you the primitives to do that, but the happy path in most tutorials stops too early.
Why React Native Linking Is More Than a URL

A link isn't just a transport mechanism. In a mobile app, it's a promise that the user will arrive in the right place with the right context preserved. When that promise fails, the app feels disconnected from the web, from notifications, and from shared content.
React Native handles this through its built-in linking model, which is why deep linking in this ecosystem should be treated as core app infrastructure, not an optional add-on. It connects push notifications, shared URLs, email campaigns, and browser routes to actual in-app screens. If your app already has onboarding branches, auth gates, or content behind permissions, linking becomes part of your product logic.
Why teams care beyond navigation
The business case is no longer abstract. React Native deep linking is now commonly instrumented with acquisition and reliability metrics, including Deep Link Opens, Conversion Rate, Source Attribution, Error Rate, and Time to Engagement, as described in this 2026 deep linking article. That matters because linking isn't only about opening a screen. It's about measuring whether the user completed the action that screen was supposed to drive.
Practical rule: If a deep link can open the app but you can't tell whether it led to the intended screen and action, the implementation isn't finished.
This is also why product teams increasingly connect linking work to adjacent concerns like onboarding logic, personalized experiences, and even integrating AI into mobile applications, where context passed through links can shape what a user sees on first open.
The part many teams underestimate
Most broken implementations fail at consistency. The iOS build supports one URL shape, Android supports another, and web uses a third. Users don't care why. They just see that sometimes a link works and sometimes it dumps them into a default screen.
A better approach is to define URL prefixes and route mappings as part of app architecture. That gives you one model for custom schemes, universal links, and web URLs. If you need a broader React Native architecture baseline before refining linking flows, Nerdify's guide on building apps with React Native is a useful companion.
Handling Basic Outbound and Inbound Links

The Linking API does two jobs. It sends users out of your app, and it receives URLs coming in. Both matter. Teams often focus only on inbound deep links and forget that outbound behavior needs just as much care, especially when an app opens mail, phone, maps, or a browser-based auth flow.
React Native's built-in Linking API is the official mechanism for handling URLs, with native integration through RCTLinkingManager on iOS and equivalent platform patterns on Android, which makes it a first-class framework capability according to the React Navigation deep linking documentation.
Opening links from your app
For outbound actions, start simple and defensive. openURL() is the action. canOpenURL() is the guard.
import { Linking, Alert } from 'react-native';
async function openWebsite() {
const url = 'https://example.com/help';
const supported = await Linking.canOpenURL(url);
if (!supported) {
Alert.alert('Unable to open link');
return;
}
await Linking.openURL(url);
}
async function sendEmail() {
const url = 'mailto:[email protected]?subject=App%20Support';
if (await Linking.canOpenURL(url)) {
await Linking.openURL(url);
}
}
async function callPhone() {
const url = 'tel:+1234567890';
if (await Linking.canOpenURL(url)) {
await Linking.openURL(url);
}
}
In production, the main mistake isn't syntax. It's assuming every device can handle every scheme. Some Android devices behave differently for tel: or mailto: if the expected app isn't available. Check support before opening, and handle failure in the UI instead of doing nothing without feedback.
Receiving links on cold start and warm start
Inbound handling splits into two cases:
- Cold start means the app was closed and the URL launched it
- Warm start means the app was already running or backgrounded when the URL arrived
Both paths must be wired, or you'll end up with links that work only in one app state.
import { useEffect } from 'react';
import { Linking } from 'react-native';
export function useIncomingLinks(handleUrl: (url: string) => void) {
useEffect(() => {
Linking.getInitialURL().then((url) => {
if (url) {
handleUrl(url);
}
});
const subscription = Linking.addEventListener('url', ({ url }) => {
handleUrl(url);
});
return () => subscription.remove();
}, [handleUrl]);
}
Links that only work when the app is already open aren't "mostly working." They're broken for a large share of real user journeys.
When to handle URLs manually
If your app has a very custom flow, such as auth redirects that need preprocessing, you can parse URLs before handing them to navigation.
function handleUrl(url: string) {
const parsed = new URL(url);
if (parsed.pathname.startsWith('/reset-password')) {
// custom auth logic
return;
}
// otherwise pass to navigation
}
That said, don't build a hand-rolled router for the whole app unless you have a specific reason. Typically, native handoff plus React Navigation mapping is much easier to maintain.
Configuring Deep Links for iOS and Android

The most dependable production setup follows a simple pipeline. Native handoff first, JavaScript routing second. If either side is incomplete, the app may open and still fail to transition. That's the failure pattern called out in LogRocket's React Native deep linking guide, and it's exactly what shows up in real projects.
Choose your link types carefully
You usually support two categories:
| Link type | Example | Good for | Trade-off |
|---|---|---|---|
| Custom scheme | myapp://product/abc |
Fast setup, internal testing, fallback support | Only works when the app is installed |
| Universal Link or App Link | https://example.com/product/abc |
Better user experience, one shareable URL for app and web | Requires stricter native and server coordination |
For shipping apps, I usually keep both. HTTPS links become the primary public entry point. A custom scheme stays available for internal tools, QA, and certain partner integrations.
iOS setup that usually breaks first
On iOS, teams often register a URL scheme and stop there. That covers custom schemes, but not the broader universal link flow many products want.
For a custom scheme, register it in Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.example.app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
Then make sure the app delegate forwards incoming URLs:
#import <React/RCTLinkingManager.h>
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
For universal links, iOS also needs the Associated Domains capability enabled in Xcode. If that entitlement is missing, the domain can be perfectly configured elsewhere and the app still won't receive the link.
A common iOS false positive is this: tapping the link opens your website, so everyone assumes the domain setup is fine. For app routing, that's not enough. The app-domain association has to succeed too.
Android setup that usually fails silently
Android relies on intent filters in AndroidManifest.xml. If the filter doesn't match the incoming URL exactly enough, Android won't hand the link to your app.
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<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>
<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="example.com"
android:pathPrefix="/product" />
</intent-filter>
</activity>
The launchMode="singleTask" piece matters because it helps route links into the existing activity rather than creating confusing duplicate behavior.
The production checklist
When a team asks why links open the app but not the destination screen, I check this order:
- Scheme or domain exists in native config
- iOS forwarding methods are wired to
RCTLinkingManager - Android intent filters match the URLs
- React Navigation mapping exists for every path
- Cold and warm starts both work
- Nested routes parse params correctly
If you want a second reference specifically focused on path mapping and platform behavior, Nerdify's guide to React Native deep linking covers the broader app-linking setup around these native pieces.
Integrating Linking with React Navigation

Once the OS hands your app a URL, you still need to translate that URL into a navigation state. That's where most React Native apps should lean on React Navigation's linking prop instead of custom switch statements scattered across screens.
A linking config that scales
A good config does three things:
- declares every URL prefix your app accepts
- maps paths to screen names
- handles nested routes and parameter parsing in one place
import { NavigationContainer } from '@react-navigation/native';
const linking = {
prefixes: ['myapp://', 'https://example.com', 'https://www.example.com'],
config: {
screens: {
Home: '',
Product: 'product/:productId',
Profile: 'user/:userId',
Settings: 'settings',
},
},
};
export default function App() {
return (
<NavigationContainer linking={linking}>
{/* navigators */}
</NavigationContainer>
);
}
This setup matters because your URL contract becomes explicit. Instead of hoping a screen can infer what to do, you define the route shape in one place.
Parse parameters on purpose
Real apps rarely stop at flat strings. You might need IDs, slugs, booleans, or query parameters. Put the translation logic in the linking config so your screens receive clean values.
const linking = {
prefixes: ['myapp://', 'https://example.com'],
config: {
screens: {
Product: {
path: 'product/:productId',
parse: {
productId: (id: string) => id,
},
},
Profile: {
path: 'user/:userId',
parse: {
userId: (id: string) => id,
},
},
},
},
};
If you're dealing with nested navigators, don't take shortcuts. Explicit nested mapping is more verbose, but it prevents the class of bugs where the app lands in the right stack and the wrong child screen.
What works well in larger apps
For apps with auth flows, tabs, and modal routes, centralization wins. I prefer keeping route definitions close to the navigation container and documenting the public URL surface as part of that file.
A practical pattern looks like this:
| Concern | Recommended approach |
|---|---|
| Public content routes | Use clean HTTPS paths that can also exist on web |
| Private screens | Route through auth checks after link receipt |
| Temporary campaigns | Add explicit paths, then remove them cleanly after the campaign ends |
| Legacy URLs | Keep redirect logic in one parser layer instead of inside screens |
Navigation rule: If product teams can create or request links, engineers should maintain a single source of truth for what those links resolve to.
This becomes even more important when you're recording demos or QA walkthroughs. If you're documenting app flows for marketing or release review, a guide to polished React Native app previews is useful because linking bugs often show up immediately in screen recordings.
Two mistakes that create hard-to-debug behavior
First, teams define prefixes but forget a matching screen path. The app opens, the URL arrives, and nothing visible happens. That looks random until you inspect the config.
Second, teams mix manual URL handling with React Navigation handling for the same route family. That creates duplicate navigation calls and race conditions, especially on warm start.
If you need custom interception, keep the rule simple:
- let React Navigation own normal screen routing
- intercept only flows that necessitate preprocessing, such as auth tokens or one-time actions
That separation keeps react native linking maintainable as the app grows.
How to Properly Test Your Linking Implementation
Most linking bugs survive because teams test one scenario. They tap a link while the app is open, see the expected screen, and move on. Production users don't behave that neatly.
Test cold start and warm start separately. They use different code paths, and both break in different ways.
iOS simulator checks
On iOS, use the simulator command line instead of relying only on Safari taps:
xcrun simctl openurl booted "myapp://product/abc"
You can also test your HTTPS path the same way:
xcrun simctl openurl booted "https://example.com/product/abc"
For cold-start testing, fully terminate the app before running the command. For warm-start testing, send the app to the background and run it again. If only one state works, your native forwarding and JS subscription flow need another pass.
Android emulator checks
Android has an equivalent command through adb:
adb shell am start -W -a android.intent.action.VIEW -d "myapp://product/abc" com.example.app
And for HTTPS links:
adb shell am start -W -a android.intent.action.VIEW -d "https://example.com/product/abc" com.example.app
These commands are more reliable than ad hoc tapping because they remove guesswork about what the OS attempted to open.
A test plan worth keeping in your repo
The fastest teams treat linking tests like a checklist, not a one-time setup task. A compact version looks like this:
- Cold start route resolution verifies
getInitialURL()or the navigation container's initial URL handling - Warm start event handling confirms the subscription path updates the current app state
- Parameter parsing checks route params, slugs, and query values
- Fallback behavior confirms unsupported or malformed URLs don't crash the app
- Auth-gated routes ensures users land in the correct post-login destination
Broken deep links often hide behind successful app launches. Test the destination screen, not just whether the icon opened.
If your team already uses formal release verification, it's worth adding linking scenarios into a broader mobile app testing checklist so they don't disappear between sprints.
Common Pitfalls and Advanced Scenarios
A lot of developers assume react native linking is done once the app can catch a URL. That's only true for the already-installed case. The harder problems show up later, when links involve real campaigns, install flows, and platform-specific verification behavior.
The failures I see most often
Some issues are predictable enough that you can almost diagnose them from the symptom alone.
- iOS opens Safari instead of the app. This usually points to universal link association problems, not JavaScript navigation code.
- Android never offers the app as a handler. That often means the intent filter is too narrow, too broad in the wrong way, or doesn't match the actual host and path structure being used.
- The app opens but lands nowhere useful. Native handoff worked. Route mapping didn't.
- Only debug builds behave correctly. Release-specific signing and platform verification differences can change the result.
When troubleshooting, separate the path into stages: OS recognition, native handoff, JS receipt, navigation resolution, and post-navigation business logic. If you debug all of that at once, every failure looks the same.
The post-install gap most tutorials skip
The built-in Linking API doesn't solve everything. A major underserved angle is the post-install gap, where a user taps a link, goes through the App Store or Play Store, installs the app, and should still land in the original context. That's the role of deferred deep linking, and as noted in this discussion of deferred deep linking in React Native, it requires separate provider support or infrastructure beyond React Native's built-in API.
That distinction matters because many teams unintentionally promise deferred behavior while only implementing standard deep links. Those are not the same thing.
If marketing needs attribution and destination continuity after install, standard Linking isn't enough on its own.
What to do instead
When a product needs post-install continuity, design the flow as a separate capability:
| Scenario | Core Linking API | Extra infrastructure needed |
|---|---|---|
| User already has app installed | Usually yes | Not always |
| User clicks campaign link and installs app first | No | Yes |
| Referral flows with attribution context | Partially | Usually yes |
| First-open routing after store install | No | Yes |
This is also the point where architecture and ownership matter. Mobile engineering can wire screen routing, but campaign attribution may involve growth, backend, and analytics teams too. One option among others is bringing in a partner like Nerdify for the implementation side when the work spans native configuration, navigation, testing, and release handling across platforms.
The practical takeaway is simple. Standard linking solves installed-app routing well when it's configured carefully. It doesn't solve every business journey. Knowing where the core API stops is part of building a reliable mobile product.
React native linking works best when you treat it like a product surface, not a utility method. Get the native handoff right, keep URL-to-screen mappings explicit, test cold and warm starts separately, and don't confuse standard deep links with deferred deep linking. That's what makes links dependable in real apps, not just in demos.