Linking
Linking gives you a general interface to interact with both incoming and outgoing app links.
Every Link (URL) has a URL Scheme, some websites are prefixed with [https://](https://) or [http://](http://) and the http is the URL Scheme. Let's call it scheme for short.
In addition to https, you're likely also familiar with the mailto scheme. When you open a link with the mailto scheme, your operating system will open an installed mail application. Similarly, there are schemes for making phone calls and sending SMS. Read more about built-in URL schemes below.
Like using the mailto scheme, it's possible to link to other applications by using custom url schemes. For example, when you get a Magic Link email from Slack, the Launch Slack button is an anchor tag with an href that looks something like: slack://secret/magic-login/other-secret. Like with Slack, you can tell the operating system that you want to handle a custom scheme. When the Slack app opens, it receives the URL that was used to open it. This is often referred to as deep linking. Read more about how to get the deep link into your app.
A custom URL scheme isn't the only way to open your application on mobile. For example, if you want to email someone a link to be opened on mobile, using a custom URL scheme isn't ideal because the user might open the email on a desktop, where the link wouldn't work. Instead, you should use standard https links, such as [https://www.myapp.io/records/1234546.](https://www.myapp.io/records/1234546`.) On mobile, these links can be configured to open your app. On Android, this feature is called Deep Links, while on iOS, it is known as Universal Links.
Built-in URL Schemes
As mentioned in the introduction, there are some URL schemes for core functionality that exist on every platform. The following is a non-exhaustive list, but covers the most commonly used schemes.
| Scheme | Description | iOS | Android | Vega |
|---|---|---|---|---|
mailto |
Open mail app, e.g., mailto:support@amazon.com |
✅ | ✅ | ❌ |
tel |
Open phone app, e.g., tel:+123456789 |
✅ | ✅ | ❌ |
sms |
Open SMS app, e.g., sms:+123456789 |
✅ | ✅ | ❌ |
{http/https}://<host>[<path>][?query_params] |
Open web browser app or another app registered to handle the URL, e.g., https://amazon.com |
✅ | ✅ | ✅ |
pkg://<component-id> |
Allows you to directly address lifecycle components by name, e.g., pkg://com.amazon.lcm.test.main |
❌ | ❌ | ✅ |
os://<use-case>[<sub-page>] |
Offers a platform-independent method for accessing core OS functions, e.g., os://settings/wifi |
❌ | ❌ | ✅ |
{amzns/amzn}://apps[/path]?<identifier_type>=<identifier_value> |
Used by the package manager to launch an app via identifier, e.g., amzns://apps?asin=ABCD1234 |
❌ | ❌ | ✅ |
<custom-scheme>://[<host>[<path>]][?query_params] |
App-defined schemes for launching apps from another app/site, e.g., livetv://watchnow |
✅ | ✅ | ✅ |
broadcast://*/<reverse-dns-namespace>/<topic>/<sub-topic> |
Event topic for publishing messages to all subscribers, e.g., broadcast://*/com.amazon.idle/state/idle/screensaver |
❌ | ❌ | ✅ |
unicast://*/<reverse-dns-namespace>.<topic>/<sub-topic> |
Event topic for publishing messages to specific subscriber subsets, e.g., unicast://*/com.amazon.push-service/force-ota |
❌ | ❌ | ✅ |
fos://<package-id>[<scheme>[<host>[<path>]]][#<intent-fragment>] |
Intent-style URIs for backward compatibility, e.g., fos://com.netflix#Intent;action=OPEN_NETFLIX_ACTION;end; |
❌ | ❌ | ✅ |
Enabling Deep Links
If you want to enable deep links in your app, please read the below guide:
If you wish to receive the intent in an existing app component, you may set the launch-uris in the manifest.toml file.
[[offers.interaction]]
id = "com.amazon.ambienthome.screensaver"
launch-uris = ["os://ambient-component", "tahoe://main", "os://home", "amzns://apps"]
If you wish to receive the intent in an existing instance of MainActivity, you may set the launchMode of MainActivity to singleTask in AndroidManifest.xml. See <activity> documentation for more information.
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
iOS Setup
On iOS, you'll need to add the LinkingIOS folder into your header search paths as described in step 3 here.
If you also want to listen to incoming app links during your app's execution, you'll need to add the following lines to your AppDelegate.mm:
// iOS 9.x or newer
#import <React/RCTLinkingManager.h>
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
If your app is using Universal Links, you'll need to add the following code as well:
- (BOOL)application:(UIApplication *)application
continueUserActivity:(nonnull NSUserActivity *)userActivity
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
Handling Deep Links
There are two ways to handle URLs that open your app.
-
If the app is already open, the app is foregrounded and a Linking 'url' event is fired
You can handle these events with
Linking.addEventListener('url', callback)- it callscallback({url})with the linked URL -
If the app is not already open, it is opened and the url is passed in as the initialURL
You can handle these events with
Linking.getInitialURL()- it returns a Promise that resolves to the URL, if there is one.
Example
Open Links and Deep Links (Universal Links)
import React, {useCallback} from 'react';
import {Alert, Button, Linking, StyleSheet, View} from 'react-native';
const supportedURL = 'https://google.com';
const unsupportedURL = 'slack://open?team=123456';
const OpenURLButton = ({url, children}) => {
const handlePress = useCallback(async () => {
const supported = await Linking.canOpenURL(url);
if (supported) {
await Linking.openURL(url);
} else {
Alert.alert(`Don't know how to open this URL: ${url}`);
}
}, [url]);
return <Button title={children} onPress={handlePress} />;
};
const App = () => {
return (
<View style={styles.container}>
<OpenURLButton url={supportedURL}>Open Supported URL</OpenURLButton>
<OpenURLButton url={unsupportedURL}>Open Unsupported URL</OpenURLButton>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
import React, {useCallback} from 'react';
import {Alert, Button, Linking, StyleSheet, View} from 'react-native';
const supportedURL = 'https://google.com';
const unsupportedURL = 'slack://open?team=123456';
type OpenURLButtonProps = {
url: string;
children: string;
};
const OpenURLButton = ({url, children}: OpenURLButtonProps) => {
const handlePress = useCallback(async () => {
const supported = await Linking.canOpenURL(url);
if (supported) {
await Linking.openURL(url);
} else {
Alert.alert(`Don't know how to open this URL: ${url}`);
}
}, [url]);
return <Button title={children} onPress={handlePress} />;
};
const App = () => {
return (
<View style={styles.container}>
<OpenURLButton url={supportedURL}>Open Supported URL</OpenURLButton>
<OpenURLButton url={unsupportedURL}>Open Unsupported URL</OpenURLButton>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
Open Custom Settings
import React, {useCallback} from 'react';
import {Button, Linking, StyleSheet, View} from 'react-native';
const OpenSettingsButton = ({children}) => {
const handlePress = useCallback(async () => {
await Linking.openSettings();
}, []);
return <Button title={children} onPress={handlePress} />;
};
const App = () => {
return (
<View style={styles.container}>
<OpenSettingsButton>Open Settings</OpenSettingsButton>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
import React, {useCallback} from 'react';
import {Button, Linking, StyleSheet, View} from 'react-native';
type OpenSettingsButtonProps = {
children: string;
};
const OpenSettingsButton = ({children}: OpenSettingsButtonProps) => {
const handlePress = useCallback(async () => {
await Linking.openSettings();
}, []);
return <Button title={children} onPress={handlePress} />;
};
const App = () => {
return (
<View style={styles.container}>
<OpenSettingsButton>Open Settings</OpenSettingsButton>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
Get the Deep Link
import React, {useState, useEffect} from 'react';
import {Linking, StyleSheet, Text, View} from 'react-native';
const useInitialURL = () => {
const [url, setUrl] = useState(null);
const [processing, setProcessing] = useState(true);
useEffect(() => {
const getUrlAsync = async () => {
const initialUrl = await Linking.getInitialURL();
setTimeout(() => {
setUrl(initialUrl);
setProcessing(false);
}, 1000);
};
getUrlAsync();
}, []);
return {url, processing};
};
const App = () => {
const {url: initialUrl, processing} = useInitialURL();
return (
<View style={styles.container}>
<Text>
{processing
? 'Processing the initial url from a deep link'
: `The deep link is: ${initialUrl || 'None'}`}
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
import React, {useState, useEffect} from 'react';
import {Linking, StyleSheet, Text, View} from 'react-native';
const useInitialURL = () => {
const [url, setUrl] = useState<string | null>(null);
const [processing, setProcessing] = useState(true);
useEffect(() => {
const getUrlAsync = async () => {
const initialUrl = await Linking.getInitialURL();
setTimeout(() => {
setUrl(initialUrl);
setProcessing(false);
}, 1000);
};
getUrlAsync();
}, []);
return {url, processing};
};
const App = () => {
const {url: initialUrl, processing} = useInitialURL();
return (
<View style={styles.container}>
<Text>
{processing
? 'Processing the initial url from a deep link'
: `The deep link is: ${initialUrl || 'None'}`}
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
Send Intents (Android)
import React, {useCallback} from 'react';
import {Alert, Button, Linking, StyleSheet, View} from 'react-native';
const SendIntentButton = ({action, extras, children}) => {
const handlePress = useCallback(async () => {
try {
await Linking.sendIntent(action, extras);
} catch (e) {
Alert.alert(e.message);
}
}, [action, extras]);
return <Button title={children} onPress={handlePress} />;
};
const App = () => {
return (
<View style={styles.container}>
<SendIntentButton action="android.intent.action.POWER_USAGE_SUMMARY">
Power Usage Summary
</SendIntentButton>
<SendIntentButton
action="android.settings.APP_NOTIFICATION_SETTINGS"
extras={[
{
key: 'android.provider.extra.APP_PACKAGE',
value: 'com.facebook.katana',
},
]}>
App Notification Settings
</SendIntentButton>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
import React, {useCallback} from 'react';
import {Alert, Button, Linking, StyleSheet, View} from 'react-native';
type SendIntentButtonProps = {
action: string;
children: string;
extras?: Array<{
key: string;
value: string | number | boolean;
}>;
};
const SendIntentButton = ({
action,
extras,
children,
}: SendIntentButtonProps) => {
const handlePress = useCallback(async () => {
try {
await Linking.sendIntent(action, extras);
} catch (e: any) {
Alert.alert(e.message);
}
}, [action, extras]);
return <Button title={children} onPress={handlePress} />;
};
const App = () => {
return (
<View style={styles.container}>
<SendIntentButton action="android.intent.action.POWER_USAGE_SUMMARY">
Power Usage Summary
</SendIntentButton>
<SendIntentButton
action="android.settings.APP_NOTIFICATION_SETTINGS"
extras={[
{
key: 'android.provider.extra.APP_PACKAGE',
value: 'com.facebook.katana',
},
]}>
App Notification Settings
</SendIntentButton>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
Reference
Methods
addEventListener()
static addEventListener(
type: 'url',
handler: (event: {url: string}) => void,
): EmitterSubscription;
Add a handler to Linking changes by listening to the url event type and providing the handler.
canOpenURL()
static canOpenURL(url: string): Promise<boolean>;
Determine whether or not an installed app can handle a given URL.
The method returns a Promise object. When it is determined whether or not the given URL can be handled, the promise is resolved and the first parameter is whether or not it can be opened.
The Promise will reject on Android and Vega if it was impossible to check if the URL can be opened or when targeting Android 11 (SDK 30) if you didn't specify the relevant intent queries in AndroidManifest.xml. Similarly on iOS, the promise will reject if you didn't add the specific scheme in the LSApplicationQueriesSchemes key inside Info.plist (see bellow).
Parameters:
| Name | Type | Description |
|---|---|---|
| url Required | string | The URL to open. |
<manifest ...>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https"/>
</intent>
</queries>
</manifest>
getInitialURL()
static getInitialURL(): Promise<string | null>;
If the app launch was triggered by an app link, it will give the link url, otherwise it will give null.
getInitialURL may return null when Remote JS Debugging is active. Disable the debugger to ensure it gets passed.openSettings()
static openSettings(): Promise<void>;
Open the Settings app and displays the app’s custom settings, if it has any.
openURL()
static openURL(url: string): Promise<any>;
Try to open the given url with any of the installed apps.
You can use other URLs, like a location (e.g. "geo:37.484847,-122.148386" on Android or "https://maps.apple.com/?ll=37.484847,-122.148386" on iOS), a contact, or any other URL that can be opened with the installed apps.
The method returns a Promise object. If the user confirms the open dialog or the url automatically opens, the promise is resolved. If the user cancels the open dialog or there are no registered applications for the url, the promise is rejected.
Parameters:
| Name | Type | Description |
|---|---|---|
| url Required | string | The URL to open. |
sendIntent() Android
static sendIntent(
action: string,
extras?: Array<{key: string; value: string | number | boolean}>,
): Promise<void>;
Launch an Android intent with extras.
Parameters:
| Name | Type |
|---|---|
| action Required | string |
| extras | Array<{key: string, value: string | number | boolean}> |
Last updated: Jun 22, 2026

