# Deep links in a React Native app

`@mocco/react-native/links` does three things in your app:

- When a Mocco link opens the app, it tells you which screen to show.
- On Android, when someone installed the app from a link, it finds that link at first launch.
- It makes share links for content in your app.

It has no native code of its own. You pass in a few things your app already has.

## Before you start

- Set up your link host and your app's details, and make links open the app (see [Make links open the app](https://www.mocco.club/docs/en/articles/ikc6rm-open-the-app)). Without that, the phone opens links in the browser instead of your app.
- Create a **Publishable** key with `links:app` under **API keys** in the project (see [Create links from your server or app](https://www.mocco.club/docs/en/articles/wmyyuw-api)). It is safe to ship in the app.

## Install

```sh
npx expo install @mocco/react-native @react-native-async-storage/async-storage expo-application
```

In a bare React Native app, install `@mocco/react-native`, `@react-native-async-storage/async-storage` and `react-native-play-install-referrer` instead.

## Set it up

Create it once, when the app starts:

```ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Application from 'expo-application';
import { Linking, Platform } from 'react-native';
import { createMoccoLinks } from '@mocco/react-native/links';

export const links = createMoccoLinks({
  publishableKey: 'mk_pub_…',
  // Your link hosts: your own link domain and Mocco's host for the project.
  domains: ['go.example.com', 'acme.<link domain>'],
  linking: Linking,
  storage: AsyncStorage,
  appVersion: Application.nativeApplicationVersion ?? '0.0.0',
  platform: Platform.OS,
  installReferrer: Platform.OS === 'android' ? () => Application.getInstallReferrerAsync() : undefined,
  onError: error => console.warn('Mocco links', error),
});
```

In a bare app, read the referrer with `react-native-play-install-referrer` and pass a function that returns its `installReferrer` string.

Use the hosts shown on the **Deep links** settings page. Links on other hosts, and your app's own URL scheme, are left to your app's usual handling.

## Open the right screen

`onLink` hears the link that started the app and every link that opens it later. Each event has the path into your app (`path`), the link's `params` and `campaign`, and `source`: `link` when the phone opened the app with the link, `deferred` when the link led to the install.

With React Navigation, put your link hosts in `prefixes` and hand Mocco's path to `getStateFromPath`:

```tsx
import { NavigationContainer, getStateFromPath } from '@react-navigation/native';

const linking = {
  prefixes: ['https://go.example.com', 'https://acme.<link domain>'],
  config: { screens: { Product: 'products/:id' } },
  // The phone hands the app the short link; Mocco answers its path.
  async getInitialURL() {
    return null;
  },
  subscribe(listener: (url: string) => void) {
    const stop = links.onLink(event => {
      const query = new URLSearchParams(event.params).toString();
      listener(`https://go.example.com${event.path}${query === '' ? '' : `?${query}`}`);
    });
    return stop;
  },
  getStateFromPath,
};

export function App() {
  return <NavigationContainer linking={linking}>{/* … */}</NavigationContainer>;
}
```

`getInitialURL` returns `null` because `onLink` already delivers the link that started the app, including one that arrives before your first screen subscribes.

Call `links.resolveDeferred()` once your first screen is ready. On Android, the first launch after an install from a link opens the linked screen through the same `onLink` listener. It asks Mocco only once per install and never throws: if anything goes wrong, the app just opens as usual.

On iOS, the link's landing page copies a one-time code when someone taps **Get the app** (see [Deferred links on iOS](https://www.mocco.club/docs/en/articles/hyaqqz-deferred#deferred-links-on-ios)). To pick it up at first launch, add `expo-clipboard` and opt in:

```ts
import * as Clipboard from 'expo-clipboard';

export const links = createMoccoLinks({
  // …as above
  ios: { readClipboardOnFirstLaunch: true, clipboard: Clipboard },
});
```

`resolveDeferred()` then reads the clipboard once, at first launch only, and uses it only when it holds exactly a Mocco code. It never writes or clears the clipboard. Reading may show iOS's paste prompt; to avoid it, leave the option off and show expo-clipboard's `ClipboardPasteButton` on your first screen instead:

```tsx
<Clipboard.ClipboardPasteButton
  acceptedContentTypes={['plain-text']}
  onPress={async data => {
    if (data.type === 'text') {
      await links.resolveFromPaste(data.text);
    }
  }}
/>
```

Without either, `resolveDeferred()` on iOS records the first launch and answers `none`.

## Make a share link

```ts
const { url } = await links.createLink({
  deepLinkPath: '/products/42',
  params: { ref: 'share' },
  og: { title: 'Ceramic mug', description: 'Hand-made, 350 ml' },
});
```

The same content gets the same link back, so you can make the link each time someone taps **Share**. Share links always fall back to your app's store pages. `createLink` throws when the link can't be made, for example when the project has made too many share links today.

## What's kept on the phone

The SDK keeps two values in the storage you pass in: a random install ID that it makes once (not a device identifier), and the answer to the first-launch check. Mocco doesn't store the phone's IP address or browser details.

## Test it

- **A link that opens the app:** install a build that includes your app's link details, then tap a link in Notes or Messages. A link pasted into the browser's address bar opens the web page instead.
- **iOS installs from a link:** this needs the App Store or TestFlight. Delete the app, open a link in Safari on the iPhone, tap **Get the app**, install, and open the app. It should land on the linked screen. Then tap **Open in app** on the same page with the app installed: it should open the app.
- **Android installs from a link:** this needs Google Play, so use an internal testing track. Uninstall the app, tap a link on the phone, install from Play, and open the app. It should land on the linked screen.
- **Logs:** pass `onError` while testing. A link that Mocco doesn't know, or a network error, shows up there.

## Related

- [Make links open the app](https://www.mocco.club/docs/en/articles/ikc6rm-open-the-app)
- [Deferred deep links](https://www.mocco.club/docs/en/articles/hyaqqz-deferred) — the same calls for apps without the SDK
- [Create links from your server or app](https://www.mocco.club/docs/en/articles/wmyyuw-api)
