---
title: Deep Linking
description: Set your Tauri application as the default handler for an URL.
plugin: deep-link
i18nReady: true
---

import PluginLinks from '@components/PluginLinks.astro';
import Compatibility from '@components/plugins/Compatibility.astro';

import { Tabs, TabItem, Steps } from '@astrojs/starlight/components';
import CommandTabs from '@components/CommandTabs.astro';
import PluginPermissions from '@components/PluginPermissions.astro';

<PluginLinks plugin={frontmatter.plugin} />

Set your Tauri application as the default handler for an URL.

## Supported Platforms

<Compatibility plugin={frontmatter.plugin} />

## Setup

Install the deep-link plugin to get started.

<Tabs>
  <TabItem label="Automatic">

    Use your project's package manager to add the dependency:

    {' '}

    <CommandTabs
      npm="npm run tauri add deep-link"
      yarn="yarn run tauri add deep-link"
      pnpm="pnpm tauri add deep-link"
      deno="deno task tauri add deep-link"
      bun="bun tauri add deep-link"
      cargo="cargo tauri add deep-link"
    />

  </TabItem>
  <TabItem label="Manual">
    <Steps>

      1. Run the following command in the `src-tauri` folder to add the plugin to the project's dependencies in `Cargo.toml`:

          ```sh frame=none
          cargo add tauri-plugin-deep-link@2.0.0
          ```

      2.  Modify `lib.rs` to initialize the plugin:

          ```rust title="src-tauri/src/lib.rs" ins={4}
          #[cfg_attr(mobile, tauri::mobile_entry_point)]
          pub fn run() {
              tauri::Builder::default()
                  .plugin(tauri_plugin_deep_link::init())
                  .run(tauri::generate_context!())
                  .expect("error while running tauri application");
          }
          ```

      3.  Install the JavaScript Guest bindings using your preferred JavaScript package manager:

          <CommandTabs
              npm="npm install @tauri-apps/plugin-deep-link"
              yarn="yarn add @tauri-apps/plugin-deep-link"
              pnpm="pnpm add @tauri-apps/plugin-deep-link"
              deno="deno add npm:@tauri-apps/plugin-deep-link"
              bun="bun add @tauri-apps/plugin-deep-link"
          />

    </Steps>

  </TabItem>
</Tabs>

## Setting up

### Android

There are two ways to open your app from links on Android:

1. **App Links (http/https + host, verified)**
   For [app links](https://developer.android.com/training/app-links#android-app-links), you need a server with a
   `.well-known/assetlinks.json` endpoint that must return a text response in the given format:

```json title=".well-known/assetlinks.json"
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "$APP_BUNDLE_ID",
      "sha256_cert_fingerprints": [
        $CERT_FINGERPRINT
      ]
    }
  }
]
```

Where `$APP_BUNDLE_ID` is the value defined on [`tauri.conf.json > identifier`] with `-` replaced with `_` and
`$CERT_FINGERPRINT` is a list of SHA256 fingerprints of your app's signing certificates,
see [verify Android applinks] for more information.

2. **Custom URI schemes (no host required, no verification)**
   For URIs like `myapp://...`, you can declare a custom scheme without hosting any files. Use the `scheme` field in the mobile configuration and omit the `host`.

### iOS

There are two ways to open your app from links on iOS:

1. **Universal Links (https + host, verified)**
   For [universal links], you need a server with a `.well-known/apple-app-site-association` endpoint that must return a JSON response
   in the given format:

```json title=".well-known/apple-app-site-association"
{
  "applinks": {
    "details": [
      {
        "appIDs": ["$DEVELOPMENT_TEAM_ID.$APP_BUNDLE_ID"],
        "components": [
          {
            "/": "/open/*",
            "comment": "Matches any URL whose path starts with /open/"
          }
        ]
      }
    ]
  }
}
```

:::note
The response `Content-Type` header must be `application/json`.

The `.well-known/apple-app-site-association` endpoint must be served over HTTPS.
To test localhost you can either use a self-signed TLS certificate and install it on the iOS simulator or use services like [ngrok].
:::

Where `$DEVELOPMENT_TEAM_ID` is the value defined on `tauri.conf.json > tauri > bundle > iOS > developmentTeam` or the
`TAURI_APPLE_DEVELOPMENT_TEAM` environment variable and `$APP_BUNDLE_ID` is the value defined on [`tauri.conf.json > identifier`].

To verify if your domain has been properly configured to expose the app associations, you can run the following command,
replacing `<host>` with your actual host:

```sh
curl -v https://app-site-association.cdn-apple.com/a/v1/<host>
```

See [applinks.details](https://developer.apple.com/documentation/bundleresources/applinks/details) for more information.

2. **Custom URI schemes (no host, no verification)**
   For URIs like `myapp://...`, you can declare a custom scheme under mobile configuration with `"appLink": false` (or omit it). The plugin generates the appropriate `CFBundleURLTypes` entries in your app's Info.plist. No `.well-known` files or HTTPS host are needed.

### Desktop

On Linux and Windows deep links are delivered as a command line argument to a new app process.
The deep link plugin has integration with the [single instance] plugin if you prefer having a unique app instance receiving the events.

- First you must add the `deep-link` feature to the single instance plugin:

```toml title="src-tauri/Cargo.toml"
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\"))".dependencies]
tauri-plugin-single-instance = { version = "2.0.0", features = ["deep-link"] }
```

- Then configure the single instance plugin which should always be the first plugin you register:

```rust title="src-tauri/lib.rs"
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    let mut builder = tauri::Builder::default();

    #[cfg(desktop)]
    {
        builder = builder.plugin(tauri_plugin_single_instance::init(|_app, argv, _cwd| {
          println!("a new app instance was opened with {argv:?} and the deep link event was already triggered");
          // when defining deep link schemes at runtime, you must also check `argv` here
        }));
    }

    builder = builder.plugin(tauri_plugin_deep_link::init());
}
```

:::warn
The user could trigger a fake deep link manually by including the URL as argument.
Tauri matches the command line argument against the configured schemes to mitigate this,
but you should still check if the URL matches the format you expect.

This means Tauri only handles deep links for schemes that were statically configured,
and schemes registered at runtime must be manually checked using [`Env::args_os`].
:::

## Configuration

Under `tauri.conf.json > plugins > deep-link`, configure mobile domains/schemes and desktop schemes you want to associate with your application.

### Examples

**Custom scheme on mobile (no server required):**

```json title="tauri.conf.json"
{
  "plugins": {
    "deep-link": {
      "mobile": [
        {
          "scheme": ["ovi"],
          "appLink": false
        }
      ]
    }
  }
}
```

This registers the `ovi://*` scheme on Android and iOS.

**App Link / Universal Link (verified https + host):**

```json
{
  "plugins": {
    "deep-link": {
      "mobile": [
        {
          "scheme": ["https"],
          "host": "your.website.com",
          "pathPrefix": ["/open"],
          "appLink": true
        }
      ]
    }
  }
}
```

This registers `https://your.website.com/open/*` as an app/universal link.

**Desktop custom schemes:**

```json
{
  "plugins": {
    "deep-link": {
      "desktop": {
        "schemes": ["something", "my-tauri-app"]
      }
    }
  }
}
```

## Usage

The deep-link plugin is available in both JavaScript and Rust.

### Listening to Deep Links

<Tabs syncKey="lang">
  <TabItem label="JavaScript">

When a deep link triggers your app while it's running, the `onOpenUrl` callback is called. To detect whether your app was opened via a deep link, use `getCurrent` on app start.

```javascript
import { getCurrent, onOpenUrl } from '@tauri-apps/plugin-deep-link';
// when using `"withGlobalTauri": true`, you may use
// const { getCurrent, onOpenUrl } = window.__TAURI__.deepLink;

const startUrls = await getCurrent();
if (startUrls) {
  // App was likely started via a deep link
  // Note that getCurrent's return value will also get updated every time onOpenUrl gets triggered.
}

await onOpenUrl((urls) => {
  console.log('deep link:', urls);
});
```

  </TabItem>
  <TabItem label="Rust">

When a deep link triggers your app while it's running, the plugin's `on_open_url` closure is called. To detect whether your app was opened via a deep link, use `get_current` on app start.

```rust title="src-tauri/src/lib.rs"
use tauri_plugin_deep_link::DeepLinkExt;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_deep_link::init())
        .setup(|app| {
            // Note that get_current's return value will also get updated every time on_open_url gets triggered.
            let start_urls = app.deep_link().get_current()?;
            if let Some(urls) = start_urls {
                // app was likely started by a deep link
                println!("deep link URLs: {:?}", urls);
            }

            app.deep_link().on_open_url(|event| {
                println!("deep link URLs: {:?}", event.urls());
            });
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
```

  </TabItem>
</Tabs>

:::note
The open URL event is triggered with a list of URLs that were requested to be compatible with the macOS API for deep links,
but in most cases your app will only receive a single URL.
:::

### Registering Desktop Deep Links at Runtime

The [configuration](#configuration) section describes how to define static deep link schemes for your application.

On Linux and Windows it is possible to also associate schemes with your application at runtime via the `register` Rust function.

In the following snippet, we will register the `my-app` scheme at runtime. After executing the app for the first time,
the operating system will open `my-app://*` URLs with our application:

```rust title="src-tauri/src/lib.rs"
use tauri_plugin_deep_link::DeepLinkExt;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_deep_link::init())
        .setup(|app| {
            #[cfg(desktop)]
            app.deep_link().register("my-app")?;
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
```

:::note
Registering the deep links at runtime can be useful for developing on Linux and Windows
as by default the deep link is only registered when your app is installed.

Installing an AppImage can be complicated as it requires an AppImage launcher.

Registering the deep links at runtime might be preferred, so Tauri also includes a
helper function to force register all statically configured deep links at runtime.
Calling this function also ensures the deep links is registered for development mode:

```rust
#[cfg(any(target_os = "linux", all(debug_assertions, windows)))]
{
  use tauri_plugin_deep_link::DeepLinkExt;
  app.deep_link().register_all()?;
}
```

:::

## Testing

There are some caveats to test deep links for your application.

### Desktop

Deep links are only triggered for installed applications on desktop.
On Linux and Windows you can circumvent this using the [`register_all`] Rust function,
which registers all configured schemes to trigger the current executable:

```rust title="src-tauri/src/lib.rs"
use tauri_plugin_deep_link::DeepLinkExt;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_deep_link::init())
        .setup(|app| {
            #[cfg(any(windows, target_os = "linux"))]
            {
                use tauri_plugin_deep_link::DeepLinkExt;
                app.deep_link().register_all()?;
            }
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
```

:::note
Installing an AppImage that supports deep links on Linux requires an AppImage launcher to integrate the AppImage with the operating system.
Using the `register_all` function you can support deep links out of the box, without requiring your users to use external tools.

When the AppImage is moved to a different location in the file system, the deep link is invalidated since it leverages an absolute path
to the executable, which makes registering the schemes at runtime even more important.

See the [Registering Desktop Deep Links at Runtime](#registering-desktop-deep-links-at-runtime) section for more information.
:::

:::caution
Registering deep links at runtime is not possible on macOS, so deep links can only be tested on the bundled application,
which must be installed in the `/Applications` directory.
:::

#### Windows

To trigger a deep link on Windows you can either open `<scheme>://url` in the browser or run the following command in the terminal:

```sh
start <scheme>://url
```

#### Linux

To trigger a deep link on Linux you can either open `<scheme>://url` in the browser or run `xdg-open` in the terminal:

```sh
xdg-open <scheme>://url
```

### iOS

To trigger an app link on iOS you can open the `https://<host>/path` URL in the browser. For simulators you can leverage the `simctl` CLI to directly open a link from the terminal:

```sh
xcrun simctl openurl booted https://<host>/path
```

### Android

To trigger an app link on Android you can open the `https://<host>/path` URL in the browser. For emulators you can leverage the `adb` CLI to directly open a link from the terminal:

```sh
adb shell am start -a android.intent.action.VIEW -d https://<host>/path <bundle-identifier>
```

## Permissions

By default all potentially dangerous plugin commands and scopes are blocked and cannot be accessed. You must modify the permissions in your `capabilities` configuration to enable these.

See the [Capabilities Overview](/security/capabilities/) for more information and the [step by step guide](/learn/security/using-plugin-permissions/) to use plugin permissions.

```json title="src-tauri/capabilities/default.json" ins={9}
{
  "$schema": "../gen/schemas/mobile-schema.json",
  "identifier": "mobile-capability",
  "windows": ["main"],
  "platforms": ["iOS", "android"],
  "permissions": [
    // Usually you will need core:event:default to listen to the deep-link event
    "core:event:default",
    "deep-link:default"
  ]
}
```

<PluginPermissions plugin={frontmatter.plugin} />

[`tauri.conf.json > identifier`]: /reference/config/#identifier
[verify Android applinks]: https://developer.android.com/training/app-links/verify-android-applinks#web-assoc
[universal links]: https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content?language=objc
[single instance]: /plugin/single-instance/
[`register_all`]: https://docs.rs/tauri-plugin-deep-link/2.0.0/tauri_plugin_deep_link/struct.DeepLink.html#method.register_all
[ngrok]: https://ngrok.com/
[`Env::args_os`]: https://docs.rs/tauri/2.0.0/tauri/struct.Env.html#structfield.args_os
