BowlerKit
Features

Deep Links

8/20/2026

Learn how deep links are implemented and how to configure them for your Expo application.

Deep linking allows your app to be opened from a URL, navigating the user directly to a specific page with pre-filled data. In this boilerplate, we use Expo Router which handles deep linking automatically based on your file-based routing structure.

Current Implementation

The boilerplate currently implements deep linking primarily for the Reset Password flow.

1. Project Configuration

The custom scheme is configured in mobile-app/app.config.ts.

{
  "expo": {
    "scheme": "expolate",
    "ios": {
      "bundleIdentifier": "com.soewidiputra.expolate.mobile"
    },
    "android": {
      "package": "com.soewidiputra.expolate.mobile"
    }
  }
}
  • Scheme: expolate (This allows you to open the app using expolate://)
  • Bundle/Package: Used for identifying the app on the respective platforms.

Expo Router maps incoming URIs directly to the files in the app directory. For example:

  • expolate://reset-password maps to app/(auth)/reset-password.tsx
  • expolate://settings/appearance maps to app/settings/appearance.tsx

The routing group (auth) is ignored in the URL path, allowing for cleaner deep links.

3. Handling Parameters

Pages can receive data from deep links using the useLocalSearchParams hook from expo-router. For example, in app/(auth)/reset-password.tsx:

import { useLocalSearchParams } from 'expo-router';

export default function ResetPasswordScreen() {
    // Automatically extracts ?token=... and &email=... from the URL
    const params = useLocalSearchParams<{ token: string; email: string }>();
    
    const { token, email } = params;
    // Use token and email to verify the reset request with your backend
}

While Custom Schemes (expolate://) work out of the box, Universal Links (iOS) and App Links (Android) require additional configuration for seamless web-to-app transitions.

To support https://yourdomain.com/reset-password, add intentFilters to the android section in app.json:

"android": {
  "intentFilters": [
    {
      "action": "VIEW",
      "data": [
        {
          "scheme": "https",
          "host": "yourdomain.com",
          "pathPrefix": "/reset-password"
        }
      ],
      "category": ["BROWSABLE", "DEFAULT"]
    }
  ]
}

Add associatedDomains to the ios section in app.json:

"ios": {
  "associatedDomains": ["applinks:yourdomain.com"]
}

Note: Both require hosting verification files (assetlinks.json for Android and apple-app-site-association for iOS) on your web server.

The easiest way to test deep links during development using the uri-scheme tool:

# Test on Android
npx uri-scheme open "expolate://reset-password?token=TEST_TOKEN&email=user@example.com" --android

# Test on iOS
npx uri-scheme open "expolate://reset-password?token=TEST_TOKEN&email=user@example.com" --ios

Android (ADB)

Simulate a deep link on an Android emulator:

adb shell am start -W -a android.intent.action.VIEW \
    -d "expolate://reset-password?token=MY_TOKEN&email=user@example.com" \
    com.soewidiputra.expolate.mobile

iOS (Simulator)

Test on the iOS simulator:

xcrun simctl openurl booted "expolate://reset-password?token=MY_TOKEN&email=user@example.com"

Customizing the Scheme

To change the deep link scheme (e.g., from expolate to myapp):

  1. Update the scheme field in mobile-app/app.config.ts.

  2. If you are using a Development Build, rebuild your development client:

    npx eas build --profile development --platform android
  3. Ensure your backend (e.g., Laravel) matches this scheme when generating reset links.