npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@urlshortener/react-native-deep-linking

v0.1.0

Published

React Native / Expo SDK for deep linking with deferred installation support

Readme

@urlshortener/react-native-deep-linking

A pure JavaScript/TypeScript React Native SDK for deep linking with deferred installation support.

Features

  • Deferred Deep Linking — Match deep link payloads to fresh app installs using device fingerprinting
  • Incoming Deep Links — Resolve Universal Links (iOS) and App Links (Android) via API
  • Zero Native Modules — Pure JavaScript/TypeScript, works in both Expo managed and bare React Native apps
  • Expo Config Plugin — Automatic iOS Associated Domains + Android App Links setup for Expo projects
  • One-Time Call Guarantee — Deferred linking fires exactly once on first launch; subsequent calls are no-ops
  • Graceful Error Handling — Network failures and API errors return { matched: false } rather than throwing

Installation

npm / yarn

npm install @urlshortener/react-native-deep-linking
# or
yarn add @urlshortener/react-native-deep-linking

Peer Dependencies

npm install react react-native @react-native-async-storage/async-storage

AsyncStorage is required for the SDK to persist the one-time deferred linking flag. Install it:

npm install @react-native-async-storage/async-storage
# For Expo projects:
expo install @react-native-async-storage/async-storage

Quick Start

1. Initialize the SDK

Call configure() early in your app (e.g., in a root component or app entry point):

import { configure } from '@urlshortener/react-native-deep-linking';

configure({
  apiKey: 'YOUR_API_KEY_FROM_BACKEND',
  baseUrl: 'https://api.yourdomain.com', // optional, defaults to https://snip.sa
});

2. Handle Deferred Deep Links (First Launch)

Add the useDeferredDeepLink hook to your root component:

import React from 'react';
import { useDeferredDeepLink } from '@urlshortener/react-native-deep-linking';

export default function App() {
  const { result, loading } = useDeferredDeepLink();

  if (loading) {
    return <SplashScreen />;
  }

  // If deferred linking matched, result.screen and result.params contain the intent
  if (result?.matched) {
    // Navigate to the appropriate screen
    // e.g., navigate(result.screen, result.params)
  }

  return <MainApp />;
}

3. Handle Incoming Deep Links (Universal Links / App Links)

Add the useIncomingDeepLink hook in the same component (or wrap it separately):

import { useIncomingDeepLink } from '@urlshortener/react-native-deep-linking';

export function DeepLinkListener() {
  const { result } = useIncomingDeepLink();

  React.useEffect(() => {
    if (result?.matched) {
      // Handle the incoming deep link
      // e.g., navigate(result.screen, result.params)
    }
  }, [result]);

  return null;
}

Expo Config Plugin Setup

If you're using Expo, the config plugin automates iOS and Android deep linking configuration.

Install the Plugin

The plugin is bundled in the SDK package:

npm install @expo/config-plugins

Configure in app.json

Add the plugin to your Expo config:

{
  "expo": {
    "plugins": [
      [
        "@urlshortener/react-native-deep-linking",
        {
          "domain": "short.yourdomain.com"
        }
      ]
    ]
  }
}

The plugin will:

  • iOS: Add applinks:short.yourdomain.com to the Associated Domains entitlement
  • Android: Inject an intent-filter for HTTPS App Links with android:autoVerify="true"

Bare React Native Setup

If you're not using Expo, you must manually configure deep linking:

iOS

  1. Open ios/YourApp.xcodeproj in Xcode
  2. Select your app target → Signing & Capabilities
  3. Add "Associated Domains" capability
  4. Add applinks:short.yourdomain.com

Android

Edit android/app/src/main/AndroidManifest.xml:

<activity
  android:name=".MainActivity"
  android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
  <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="short.yourdomain.com" />
  </intent-filter>
</activity>

API Reference

configure(options: { apiKey: string; baseUrl?: string }): void

Initialize the SDK. Must be called before using any other functions.

Parameters:

  • apiKey (required) — Your app's API key from the backend
  • baseUrl (optional) — Base URL of the API (defaults to https://snip.sa)

Example:

configure({
  apiKey: 'sk_live_abc123...',
  baseUrl: 'https://api.yourdomain.com',
});

useDeferredDeepLink(): { result: DeepLinkResult | null; loading: boolean }

React hook for handling deferred deep links on first app launch.

Returns:

  • result — The matched deep link data (or null while loading)
  • loading — Whether the request is in flight

When to use: Add to your root component to handle deep links from install campaigns.

Example:

const { result, loading } = useDeferredDeepLink();

if (loading) return <Splash />;
if (result?.matched) {
  // Navigate to result.screen with result.params
}

handleDeferredDeepLink(): Promise<DeepLinkResult>

Imperative version of useDeferredDeepLink(). Useful if you need to handle deferred linking outside a React component.

Returns: Promise<DeepLinkResult>

Example:

const result = await handleDeferredDeepLink();
if (result.matched) {
  navigate(result.screen, result.params);
}

useIncomingDeepLink(): { result: DeepLinkResult | null }

React hook for listening to Universal Links (iOS) and App Links (Android).

Returns:

  • result — The resolved deep link (or null if no link received yet)

When to use: Wrap this in a component to listen for deep links while the app is running.

Example:

const { result } = useIncomingDeepLink();

useEffect(() => {
  if (result?.matched) {
    navigate(result.screen, result.params);
  }
}, [result]);

resolveDeepLink(url: string): Promise<DeepLinkResult>

Imperative API to resolve a deep link URL (e.g., from a clicked link).

Parameters:

  • url — The incoming URL (e.g., https://short.yourdomain.com/dl/abc123)

Returns: Promise<DeepLinkResult>

Example:

const deepLink = await resolveDeepLink('https://short.yourdomain.com/dl/abc123');
if (deepLink.matched) {
  navigate(deepLink.screen, deepLink.params);
}

Types

DeepLinkResult

interface DeepLinkResult {
  matched: boolean;           // Whether a deep link was found
  screen?: string;           // The screen name to navigate to
  params?: Record<string, any>; // Navigation parameters
  confidence?: number;       // Confidence score (0-1) for deferred linking
}

Error Handling

The SDK is designed to fail gracefully:

  • Network errors — Returns { matched: false } instead of throwing
  • Invalid API key — Returns { matched: false } (logs to console)
  • 404 / Deep link not found — Returns { matched: false }
  • Invalid shortCode — Returns { matched: false } (no network call made)

Always check result.matched before attempting to navigate.

One-Time Deferred Link Guarantee

The handleDeferredDeepLink() function uses AsyncStorage to ensure it fires exactly once, even if:

  • The function is called multiple times
  • The app crashes and restarts
  • The network request fails

After the first attempt (success or failure), the flag is set and all subsequent calls return { matched: false } immediately without making a network request.

To reset this behavior (for testing), clear the AsyncStorage key:

import AsyncStorage from '@react-native-async-storage/async-storage';

await AsyncStorage.removeItem('urlshortener_sdk_deferred_checked');

Testing

The SDK exports clean separation between hooks and imperative APIs, making it easy to test:

import { handleDeferredDeepLink, resolveDeepLink } from '@urlshortener/react-native-deep-linking';

// Test deferred linking
test('should handle matched deep link', async () => {
  const result = await handleDeferredDeepLink();
  expect(result.matched).toBe(true);
  expect(result.screen).toBe('product');
});

// Test URL resolution
test('should resolve deep link', async () => {
  const result = await resolveDeepLink('https://short.yourdomain.com/dl/abc123');
  expect(result.matched).toBe(true);
});

Mock @react-native-async-storage/async-storage and fetch in your test setup for full control.

Platform-Specific Notes

iOS

  • Requires applinks: associated domain entry to be configured
  • Works with both Universal Links (https) and custom URL schemes
  • SDK uses React Native's Linking.getInitialURL() to detect cold starts

Android

  • Requires android:autoVerify="true" in the App Links intent-filter
  • Must use https:// (not custom schemes like myapp:// for auto-verification)
  • SDK uses React Native's Linking.getInitialURL() and Linking.addEventListener() for detection

Troubleshooting

Deferred link not matching

Cause: The device fingerprint (IP, OS version, screen dimensions, install time) didn't match any stored payloads.

Solution: Ensure the backend captured a click with the same IP and similar device fingerprint before the install.

Deep links not opening the app

Cause: Associated Domains or App Links not properly configured.

Solution:

  • iOS: Verify applinks: entry is present in Xcode's Signing & Capabilities
  • Android: Rebuild APK/AAB after adding intent-filter, and verify on a real device (emulator may not support App Links auto-verification)

"Invalid API key" errors

Cause: The API key is missing, malformed, or inactive on the backend.

Solution: Verify the API key matches the one created in the backend app registration.

Contributing

Bug reports and PRs welcome. Run tests with:

npm test
npm run test:coverage

License

MIT