Skip to content
Tolinku
Tolinku
Sign In Start Free
Engineering · · 4 min read

Ionic Deep Linking: Complete Configuration Guide

By Tolinku Staff
|
Tolinku cross platform dashboard screenshot for engineering blog posts

Ionic apps can use either Capacitor (recommended) or Cordova as their native runtime. Deep linking configuration differs between the two, but the JavaScript-side URL handling is the same. This guide covers both approaches.

For Capacitor-specific setup, see Capacitor deep linking: setup for Ionic apps. For the cross-platform overview, see cross-platform deep linking guide for 2026.

Capacitor is the recommended runtime for Ionic apps since Ionic 5+. See the full Capacitor deep linking guide for detailed setup. The summary:

  1. Add Associated Domains entitlement in Xcode (applinks:yourdomain.com).
  2. Add Intent filters in AndroidManifest.xml.
  3. Host AASA and assetlinks.json on your domain.
  4. Use @capacitor/app plugin's appUrlOpen event in JavaScript.
import { App } from '@capacitor/app';

App.addListener('appUrlOpen', (event) => {
  const url = new URL(event.url);
  this.router.navigateByUrl(url.pathname + url.search);
});

Cordova

If your Ionic app still uses Cordova, use the ionic-plugin-deeplinks plugin (or cordova-plugin-deeplinks).

Installation

ionic cordova plugin add ionic-plugin-deeplinks --variable URL_SCHEME=myapp --variable DEEPLINK_SCHEME=https --variable DEEPLINK_HOST=yourdomain.com

Configuration

The plugin modifies the native configuration automatically:

  • iOS: Adds the Associated Domains entitlement.
  • Android: Adds Intent filters to AndroidManifest.xml.

You still need to host AASA and assetlinks.json on your domain.

import { Deeplinks } from '@awesome-cordova-plugins/deeplinks/ngx';

constructor(private deeplinks: Deeplinks) {}

initializeDeepLinks() {
  this.deeplinks.route({
    '/products/:productId': 'ProductPage',
    '/offers/:offerId': 'OfferPage',
    '/referral/:referrerId': 'ReferralPage'
  }).subscribe(
    match => {
      console.log('Deep link match:', match.$route, match.$args);
      // Navigate based on the match
      this.router.navigate([match.$route], {
        queryParams: match.$args
      });
    },
    nomatch => {
      console.warn('No deep link match:', nomatch.$link);
    }
  );
}

Migration from Cordova to Capacitor

If migrating from Cordova to Capacitor:

  1. Remove ionic-plugin-deeplinks.
  2. Follow the Capacitor deep linking setup.
  3. Replace the Cordova plugin's route() API with App.addListener('appUrlOpen', ...).
  4. The native configuration (entitlements, Intent filters) needs to be set up in the Capacitor project.

Ionic Angular Integration

Route Configuration

Define routes that match your deep link paths:

// app-routing.module.ts
const routes: Routes = [
  { path: '', component: HomePage },
  { path: 'products/:productId', component: ProductPage },
  { path: 'offers/:offerId', component: OfferPage },
  { path: 'referral/:referrerId', component: ReferralPage },
  { path: '**', redirectTo: '' }
];
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Platform } from '@ionic/angular';
import { App } from '@capacitor/app';

@Injectable({ providedIn: 'root' })
export class DeepLinkService {
  constructor(
    private router: Router,
    private platform: Platform
  ) {}

  async initialize() {
    // Only set up deep links on native platforms
    if (!this.platform.is('capacitor')) return;

    // Cold start
    const launchUrl = await App.getLaunchUrl();
    if (launchUrl?.url) {
      this.handleUrl(launchUrl.url);
    }

    // Warm start
    App.addListener('appUrlOpen', (event) => {
      this.handleUrl(event.url);
    });
  }

  private handleUrl(urlString: string) {
    try {
      const url = new URL(urlString);
      const path = url.pathname;

      // Use Angular router to navigate
      this.router.navigateByUrl(path + url.search).catch(err => {
        console.error('Navigation failed:', err);
        this.router.navigate(['/']);
      });
    } catch (e) {
      console.error('Invalid deep link URL:', urlString);
    }
  }
}

Initialize in App Component

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { DeepLinkService } from './services/deep-link.service';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
  constructor(private deepLinkService: DeepLinkService) {}

  ngOnInit() {
    this.deepLinkService.initialize();
  }
}

Ionic React Integration

// App.tsx
import { useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { App as CapApp } from '@capacitor/app';
import { isPlatform } from '@ionic/react';

const AppDeepLinks: React.FC = () => {
  const history = useHistory();

  useEffect(() => {
    if (!isPlatform('capacitor')) return;

    // Cold start
    CapApp.getLaunchUrl().then(result => {
      if (result?.url) {
        const url = new URL(result.url);
        history.push(url.pathname + url.search);
      }
    });

    // Warm start
    const listener = CapApp.addListener('appUrlOpen', event => {
      const url = new URL(event.url);
      history.push(url.pathname + url.search);
    });

    return () => { listener.then(l => l.remove()); };
  }, [history]);

  return null;
};

Ionic Vue Integration

// composables/useDeepLinks.ts
import { onMounted, onUnmounted } from 'vue';
import { useRouter } from 'vue-router';
import { App } from '@capacitor/app';
import { isPlatform } from '@ionic/vue';

export function useDeepLinks() {
  const router = useRouter();
  let listener: any;

  onMounted(async () => {
    if (!isPlatform('capacitor')) return;

    // Cold start
    const launchUrl = await App.getLaunchUrl();
    if (launchUrl?.url) {
      const url = new URL(launchUrl.url);
      router.push(url.pathname + url.search);
    }

    // Warm start
    listener = await App.addListener('appUrlOpen', (event) => {
      const url = new URL(event.url);
      router.push(url.pathname + url.search);
    });
  });

  onUnmounted(() => {
    listener?.remove();
  });
}

Testing

Browser (Development)

Navigate directly to deep link paths:

http://localhost:8100/products/abc123

Native (iOS Simulator)

xcrun simctl openurl booted "https://yourdomain.com/products/abc123"

Native (Android)

adb shell am start -a android.intent.action.VIEW \
  -d "https://yourdomain.com/products/abc123" \
  -c android.intent.category.BROWSABLE

Common Issues

Issue Cause Fix
Deep links work on Android but not iOS AASA file issue or missing entitlement Check AASA and Associated Domains
Route not found after deep link Angular/React/Vue router not matching Verify route paths match deep link paths
Works in browser but not on device Native configuration missing Check entitlements (iOS) and Intent filters (Android)
Cold start shows home then navigates Navigation timing issue Handle launch URL before initial render

Tolinku for Ionic Apps

Tolinku provides AASA and assetlinks.json hosting, removing the need to manage verification files. The Tolinku web SDK works with Ionic apps for smart banners.

For React Native deep linking, see React Native deep linking: complete setup tutorial. For the full guide, see cross-platform deep linking guide for 2026.

Get deep linking tips in your inbox

One email per week. No spam.

Ready to add deep linking to your app?

Set up Universal Links, App Links, deferred deep linking, and analytics in minutes. Free to start.