xref: /expo/docs/pages/guides/linking.mdx (revision 2f31cd71)
1---
2title: Linking
3description: Learn how to use Linking to handle a URL based on the URL scheme.
4---
5
6import { ConfigReactNative } from '~/components/plugins/ConfigSection';
7import { SnackInline } from '~/ui/components/Snippet';
8import { Terminal } from '~/ui/components/Snippet';
9import { BoxLink } from '~/ui/components/BoxLink';
10
11URLs are the most powerful way to launch native applications. Native operating systems like macOS, iOS, Android, Windows, and so on, have built-in link handling which chooses an app to handle a URL based on the _URL scheme_. The most common _URL schemes_ are `https` and `http` which are delegated to web browsers like Chrome, or Safari. Native apps, like the ones built with React Native, can implement any _URL scheme_, and the JavaScript React layer can handle the URL used to launch the corresponding native app.
12
13## Linking from your app
14
15The [`expo-linking`][expo-linking] API universally abstracts over native linking APIs (like `window.history` on web).
16
17```ts
18import * as Linking from 'expo-linking';
19
20Linking.openURL('https://expo.dev');
21```
22
23{/* TODO(EvanBacon): Maybe we should move `<A />` to `expo-linking`. */}
24
25Web browsers have additional link functionality like right-click to copy, and hover to preview. You can use the package [`@expo/html-elements`](https://www.npmjs.com/package/@expo/html-elements) to get a universal `<A />` element:
26
27<Terminal cmd={['$ npx expo install @expo/html-elements']} />
28
29```tsx
30import { A } from '@expo/html-elements';
31
32export default function App() {
33  return <A href="https://google.com">Go to Google</A>;
34}
35```
36
37This renders an `<a />` on web and a interactive `<Text />` which uses the `Linking` API on native. Routers like React Navigation have built-in [linking components](https://reactnavigation.org/docs/link) that you should use to move around your app.
38
39### Common URL schemes
40
41There 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.
42
43| Scheme           | Description                                  |
44| ---------------- | -------------------------------------------- |
45| `https` / `http` | Open web browser app, eg: `https://expo.dev` |
46| `mailto`         | Open mail app, eg: `mailto:[email protected]` |
47| `tel`            | Open phone app, eg: `tel:+123456789`         |
48| `sms`            | Open SMS app, eg: `sms:+123456789`           |
49
50On newer Android versions, include the appropriate queries in the **AndroidManifest.xml** to open links. This can be done by [creating a config plugin](/config-plugins/plugins-and-mods/#create-a-plugin). For example, the config plugin below will enable linking to phone and email apps:
51
52```js my-plugin.js
53const { withAndroidManifest } = require('@expo/config-plugins');
54
55const withAndroidQueries = config => {
56  return withAndroidManifest(config, config => {
57    config.modResults.manifest.queries = [
58      {
59        intent: [
60          {
61            action: [{ $: { 'android:name': 'android.intent.action.SENDTO' } }],
62            data: [{ $: { 'android:scheme': 'mailto' } }],
63          },
64          {
65            action: [{ $: { 'android:name': 'android.intent.action.DIAL' } }],
66          },
67        ],
68      },
69    ];
70
71    return config;
72  });
73};
74
75module.exports = withAndroidQueries;
76```
77
78You can then [import the custom config plugin](/config-plugins/plugins-and-mods/#import-a-plugin) in your project's app config.
79
80### Custom URL schemes
81
82If you know the custom scheme for another app you can link to it. Some services provide documentation for deep linking, for example the [Lyft deep linking documentation](https://developer.lyft.com/v1/docs/deeplinking) describes how to link directly to a specific pickup location and destination:
83
84```
85lyft://ridetype?id=lyft&pickup[latitude]=37.764728&pickup[longitude]=-122.422999&destination[latitude]=37.7763592&destination[longitude]=-122.4242038
86```
87
88It's possible that the user doesn't have the Lyft app installed, in which case you may want to open the App / Play Store, or let them know that they need to install it first. We recommend using the library [`react-native-app-link`](https://github.com/fiber-god/react-native-app-link) for these cases.
89
90On iOS, `Linking.canOpenURL` requires additional configuration to query other apps' linking schemes. You can use the `expo.ios.infoPlist` key in your app config (**app.json**, **app.config.js**) to specify a list of schemes your app needs to query. For example:
91
92```json
93{
94  "expo": {
95    "ios": {
96      "infoPlist": {
97        "LSApplicationQueriesSchemes": ["lyft"]
98      }
99    }
100  }
101}
102```
103
104If you don't specify this list, `Linking.canOpenURL` may return `false` regardless of whether the device has the app installed. Note that this configuration can only be tested in [development builds](/develop/development-builds/introduction/), because it requires native changes that will not be applied when testing in [Expo Go][expo-go].
105
106### Creating URLs
107
108To save you the trouble of inserting a bunch of conditionals based on the environment that you're in and hardcoding urls, we provide some helper methods in our extension of the `Linking` module. When you want to provide a service with a url that it needs to redirect back into your app, you can call `Linking.createURL()` and it will resolve to the following:
109
110- _Custom builds_: `myapp://`
111- _Development in Expo Go_: `exp://127.0.0.1:8081`. For SDK 48 and lower, the port number is `19000`.
112- _Published app in Expo Go_: `exp://u.expo.dev/[project-id]?channel-name=[channel-name]&runtime-version=[runtime-version]`
113
114You can also change the returned url by passing optional parameters into `Linking.createURL()`. These will be used by your app to receive data, which we will talk about in the next section.
115
116To pass some data to an app, you can append it as a path or query string on your url. `Linking.createURL(path, { queryParams })` will construct a working url automatically for you. Example:
117
118```ts
119const redirectUrl = Linking.createURL('path/into/app', {
120  queryParams: { hello: 'world' },
121});
122```
123
124This will resolve into the following, depending on the environment:
125
126- _Custom builds_: `myapp://path/into/app?hello=world`
127- _Development in Expo Go_: `exp://127.0.0.1:8081/--/path/into/app?hello=world`. For SDK 48 and lower, the port number is `19000`.
128- _Published app in Expo Go_: `exp://u.expo.dev/[project-id]?channel-name=[channel-name]&runtime-version=[runtime-version]/--/path/into/app?hello=world`
129
130> Notice in Expo Go that `/--/` is added to the URL when a path is specified. This indicates to Expo Go that the substring after it corresponds to the deep link path, and is not part of the path to the app itself.
131
132### In-app browsers
133
134The [`expo-linking`][expo-linking] API enables you to open a URL with the operating system's preferred application, you can use the [`expo-web-browser`](/versions/latest/sdk/webbrowser) module to open URLs with an in-app browser. In-app browsers are especially useful for secure [authentication](/guides/authentication).
135
136<Terminal cmd={['$ npx expo install expo-web-browser']} />
137
138<SnackInline label="WebBrowser vs Linking" dependencies={["expo-web-browser", "expo-linking"]}>
139
140```js
141import React from 'react';
142import { Button, View, StyleSheet } from 'react-native';
143import * as Linking from 'expo-linking';
144import * as WebBrowser from 'expo-web-browser';
145
146export default function App() {
147  return (
148    <View style={styles.container}>
149      <Button
150        title="Open URL with the system browser"
151        onPress={() => Linking.openURL('https://expo.dev')}
152        style={styles.button}
153      />
154      <Button
155        title="Open URL with an in-app browser"
156        onPress={() => WebBrowser.openBrowserAsync('https://expo.dev')}
157        style={styles.button}
158      />
159    </View>
160  );
161}
162
163const styles = StyleSheet.create({
164  container: {
165    flex: 1,
166    alignItems: 'center',
167    justifyContent: 'center',
168  },
169  button: {
170    marginVertical: 10,
171  },
172});
173```
174
175</SnackInline>
176
177## Linking to your app
178
179To link to your [development build](/develop/development-builds/introduction/) or standalone app, you need to specify a custom URL scheme for your app. You can register a scheme in your app config (**app.json**, **app.config.js**) by adding a string under the `scheme` key:
180
181```json
182{
183  "expo": {
184    "scheme": "myapp"
185  }
186}
187```
188
189Once you build and install your app, you will be able to open it with links to `myapp://`.
190
191> [Expo Prebuild](/workflow/prebuild) automatically adds the app's iOS bundle identifier/Android package as a URL scheme.
192
193<ConfigReactNative abstract>
194
195In **bare** apps, you can use the [`uri-scheme` package][n-uri-scheme] to easily add, remove, list, and open your URIs.
196
197To make your native app handle `myapp://` simply run:
198
199<Terminal cmd={['$ npx uri-scheme add myapp']} />
200
201You should now be able to see a list of all your project's schemes by running:
202
203<Terminal cmd={['$ npx uri-scheme list']} />
204
205You can test it to ensure it works like this:
206
207<Terminal
208  cmd={[
209    '# Rebuild the native apps, be sure to use an emulator',
210    '$ yarn android',
211    '$ yarn ios',
212    '',
213    '# Open a URI scheme',
214    '$ npx uri-scheme open myapp://some/redirect',
215  ]}
216  cmdCopy="yarn android && yarn ios && npx uri-scheme open myapp://some/redirect"
217/>
218
219</ConfigReactNative>
220
221### Linking to Expo Go
222
223[Expo Go][expo-go] uses the `exp://` scheme, however, if we link to `exp://` without any address afterward, it will open the app to the home screen.
224
225In development, your app will live at a url like `exp://127.0.0.1:8081` (for SDK 48 and lower, the port number is `19000`). When published, an experience will be hosted at a URL like `exp://u.expo.dev/[project-id]?channel-name=[channel-name]&runtime-version=[runtime-version]`, where `u.expo.dev/[project-id]` is the hosted URL that Expo Go fetches from.
226
227You can test this mechanism in your mobile browser by searching `exp://u.expo.dev/F767ADF57-B487-4D8F-9522-85549C39F43F?channel-name=main&runtime-version=exposdk:45.0.0`, this will redirect to your experience in the Expo Go app.
228
229By default `exp://` is replaced with `http://` when opening a URL in Expo Go. Similarly you can use `exps://` to open `https://` URLs. `exps://` does not currently support loading sites with insecure TLS certificates.
230
231### Handling links
232
233Links that launched your app can be observed using the `Linking.useURL` React hook:
234
235```tsx
236import * as Linking from 'expo-linking';
237import { Text } from 'react-native';
238
239export default function App() {
240  const url = Linking.useURL();
241
242  return <Text>URL: {url}</Text>;
243}
244```
245
246Behind the scenes this hook uses the following imperative API methods:
247
2481. The link that started the app is initially returned with: [`Linking.getInitialURL`](/versions/latest/sdk/linking/#linkinggetinitialurl)
2492. Any new links that were triggered while the app was already open are observed with: [`Linking.addEventListener('url', callback)`](/versions/latest/sdk/linking/#linkingaddeventlistenertype-handler)
250
251Learn more in the [API documentation](/versions/latest/sdk/linking).
252
253### Parsing URLs
254
255Parse the **path**, **hostname**, and **query parameters** from a URL with the `Linking.parse()` function. Unlike other URL parsing methods, this function considers nonstandard implementations like [Expo Go linking](#linking-to-expo-go). Example:
256
257```javascript
258function App() {
259  const url = Linking.useURL();
260
261  if (url) {
262    const { hostname, path, queryParams } = Linking.parse(url);
263
264    console.log(
265      `Linked to app with hostname: ${hostname}, path: ${path} and data: ${JSON.stringify(
266        queryParams
267      )}`
268    );
269  }
270
271  return null;
272}
273```
274
275## Testing URLs
276
277> Adding schemes will require a rebuilding your custom app.
278
279You can open a URL like:
280
281<Terminal
282  cmd={[
283    '# Custom builds',
284    '$ npx uri-scheme open myapp://somepath/into/app?hello=world --ios',
285    '',
286    '# Expo Go in development (adjust the `127.0.0.1:8081` to match your dev server URL)',
287    '$ npx uri-scheme open exp://127.0.0.1:8081/--/somepath/into/app?hello=world --ios',
288    '',
289    '# For SDK 48 and lower, use 19000 as the port number',
290  ]}
291/>
292
293You can _also_ open a URL by searching for it on the device's native browser. For example, opening Safari on iOS and typing `exp://` then searching will prompt you to open [Expo Go][expo-go] (if installed).
294
295## Next steps
296
297<BoxLink
298  title="Deep linking"
299  description="Setup iOS universal links and Android deep links."
300  href="/guides/deep-linking"
301/>
302
303<BoxLink
304  title="Authentication"
305  description="Use linking to implement web-based authentication."
306  href="/guides/authentication"
307/>
308
309<BoxLink
310  title="Routing"
311  description="Setup React Navigation linking for in-app routing."
312  href="https://reactnavigation.org/docs/configuring-links"
313/>
314
315[expo-go]: https://expo.dev/expo-go
316[n-uri-scheme]: https://www.npmjs.com/package/uri-scheme
317[expo-linking]: /versions/latest/sdk/linking
318