Skip to main content

React Native Getting Started

Install the @ulinkly/react-native package, connect it to your Ulinkly project, and validate on both platforms. The SDK is a native module built with the Expo Modules API — it bridges the native iOS and Android Ulinkly SDKs, so you get full feature parity (dynamic links, deferred deep linking, install attribution, session tracking) behind a single TypeScript API.

Expo Go is not supported

@ulinkly/react-native is a native module. It cannot run in the Expo Go sandbox. You must use an Expo development build (npx expo run:ios / npx expo run:android) or npx expo prebuild, or a bare React Native project.

1. Prerequisites

Development Environment

  • Node.js 18+ and a React Native 0.73+ or Expo SDK 50+ project
  • iOS: Xcode 15+ and iOS 13+ deployment target (for iOS builds)
  • Android: Android Studio, JDK 17, Min SDK 24+ (for Android builds)

Ulinkly Account Setup

  • Ulinkly account and project (create one here)
  • API key generated from Dashboard → Settings → API Keys → Generate API Key
  • Domain configured in your project (Domains → Add Domain)

Platform Configuration

  • A custom URL scheme (e.g. myapp) and your Ulinkly domain (e.g. myapp.shared.ly)
  • For Expo projects, these are wired automatically by the config plugin (step 3)
  • For bare React Native, you configure native files manually (step 3, bare RN path)

2. Install the package

npx expo install @ulinkly/react-native

Bare React Native

npm install @ulinkly/react-native
npx install-expo-modules@latest # one-time: wires Expo Modules into your existing RN project
cd ios && pod install
Why install-expo-modules?

The SDK is packaged as an Expo Module. The install-expo-modules step adds the small Expo Modules runtime to an existing bare RN app — you do not need to migrate to Expo to use it.

3. Configure Native Platforms

You have two paths depending on your project type. Expo projects use the config plugin (automatic). Bare React Native projects configure native files manually.

Add the plugin to your app.json (or app.config.js) under expo.plugins:

app.json
{
"expo": {
"plugins": [
["@ulinkly/react-native", {
"scheme": "myapp",
"domains": ["myapp.shared.ly"]
}]
]
}
}
PropTypeRequiredDescription
schemestringYesYour app's custom URL scheme (without ://). Must match what you register in the Ulinkly dashboard.
domainsstring[]NoOne or more Associated Domains / App Link hosts (e.g. ["myapp.shared.ly"]).

Then rebuild so the native changes take effect:

# Expo dev client
npx expo run:ios
npx expo run:android

# Or prebuild + your own native build
npx expo prebuild

What the plugin configures during prebuild:

  • iOS — Adds a CFBundleURLTypes entry for scheme in Info.plist; adds applinks:<domain> entries to the Associated Domains entitlement.
  • Android — Adds a custom-scheme <intent-filter> to your main activity; adds an android:autoVerify="true" HTTPS host <intent-filter> for each domain.
Multiple domains

Pass every domain you use: "domains": ["myapp.shared.ly", "links.mybrand.com"]. The plugin adds an entitlement / intent-filter entry for each.

Path B — Bare React Native (manual native setup)

Skip this section if you used the config plugin above.

Step 3.1: Configure iOS

URL Scheme — ios/<YourApp>/Info.plist

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string> <!-- your scheme, without :// -->
</array>
</dict>
</array>

With your iOS project open in Xcode, follow this guide to add URL types for custom URL schemes

Associated Domains — Xcode Signing & Capabilities

In Xcode, select your target → Signing & Capabilities+ CapabilityAssociated Domains, then add applinks:myapp.shared.ly. Or edit ios/<YourApp>/<YourApp>.entitlements directly:

<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:myapp.shared.ly</string>
</array>

With your iOS project open in Xcode, follow this guide to add the Associated Domains capability

AppDelegate wiring is automatic

After npx install-expo-modules@latest, the SDK's bundled Expo Module AppDelegate subscriber automatically intercepts Universal Link continuations and custom-scheme opens. No manual AppDelegate edits are required. If you maintain custom linking code, keep forwarding URLs through RCTLinkingManager as usual — the native module listens on the same delivery path.

Step 3.2: Configure Android

Add the intent filters inside your main <activity> in android/app/src/main/AndroidManifest.xml:

<!-- Custom scheme deep links -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" /> <!-- your scheme, without :// -->
</intent-filter>

<!-- HTTPS App Links (auto-verified) -->
<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="myapp.shared.ly" /> <!-- your domain -->
</intent-filter>
Replace the placeholders
  • Replace myapp with your registered URL scheme.
  • Replace myapp.shared.ly with the domain you configured in the Ulinkly dashboard.
  • Ulinkly automatically serves the .well-known/assetlinks.json and apple-app-site-association files for your domain — no manual hosting required.

4. Initialize the SDK

Initialize the SDK once at app startup, before mounting any screen that handles deep links. Always await initialize().

App.tsx
import { useEffect } from 'react';
import ULink from '@ulinkly/react-native';

async function initULink() {
await ULink.initialize({
apiKey: 'ULINK_API_KEY', // Replace with your API key
debug: __DEV__, // set false in production
});
}

// Call once at app startup — before rendering screens that need deep links.
initULink().catch(console.error);

export default function App() {
useEffect(() => {
// Subscribe to incoming deep links (cold-start and foreground).
const dynSub = ULink.onDynamicLink((data) => {
console.log('Dynamic link received:', data.parameters);
// Navigate based on data.parameters
});

const uniSub = ULink.onUnifiedLink((data) => {
console.log('Unified link received:', data);
});

return () => {
dynSub.remove();
uniSub.remove();
// Do NOT call ULink.dispose() here — see the Receive Links guide.
};
}, []);

return <YourNavigator />;
}
Initialization timing
  • initialize() is asynchronous — always await it.
  • On Android, any method called before initialize() resolves is rejected with a ULinkError.
  • On iOS, pre-init calls are queued, but order is undefined — await init for predictable behavior on both platforms.
  • initialize() is idempotent; subsequent calls are no-ops.

5. Configure Dashboard Settings

Now match your app's settings in the Ulinkly dashboard. These are required for Ulinkly to generate the verification files that enable Universal Links (iOS) and App Links (Android).

iOS Configuration

Navigate to Configuration → General → iOS.

Bundle Identifier

  • Expo: your ios.bundleIdentifier from app.json.
  • Bare RN: found in Xcode under your target's General tab (PRODUCT_BUNDLE_IDENTIFIER).

Enter it in the Bundle ID field.

Team ID

Your Apple Developer Team ID (a 10-character string like ABC123DEF4) is required for Universal Links. Find it at Apple Developer → Membership, or in Xcode under Signing & Capabilities. Enter it in the Team ID field.

URL Scheme

Enter the scheme you configured in step 3 (e.g. myapp, without ://).

Android Configuration

Navigate to Configuration → General → Android.

Package Name

  • Expo: your android.package from app.json.
  • Bare RN: the namespace in android/app/build.gradle (or package in AndroidManifest.xml).

Enter it in the Package Name field.

SHA-256 Certificate Fingerprints

SHA-256 fingerprints are required for Android App Links verification.

# From your android/ directory
cd android && ./gradlew signingReport

# Or, for the debug keystore directly:
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android | grep SHA-256

Add a fingerprint for every signing key — debug, release, and Play App Signing (Play Console → Setup → App signing).

URL Scheme (Optional)

If you configured a custom scheme, enter it (e.g. myapp, without ://).

Domain Configuration

  1. Go to the Domains section.
  2. Add your domain if needed (Shared .shared.ly subdomain, or a custom domain with DNS verification).
  3. Ensure the domain matches the domains value in your config plugin (Expo) or your Associated Domains / intent-filter host (bare RN).
Matching configuration

Every setting must match exactly between your app and the dashboard:

  • iOS: Bundle ID, Team ID, URL Scheme, Domain
  • Android: Package Name, SHA-256 Fingerprints, URL Scheme, Domain

If any don't match, deep links open in the browser instead of your app.

  1. Links → Create Link in the dashboard.
  2. Choose Unified (store redirect) or Dynamic (deep link parameters).
  3. Save and copy the short URL.

You can also create links programmatically — see Create Dynamic Links → React Native.

7. Test on devices

  1. Build and run a development build on each platform:
    npx expo run:ios       # or npx expo run:android
    (For bare RN: npx react-native run-ios / run-android.)
  2. Tap the short URL, or trigger it from the terminal:
    # Android
    adb shell am start -a android.intent.action.VIEW -d "https://myapp.shared.ly/your-slug"

    # iOS Simulator
    xcrun simctl openurl booted "https://myapp.shared.ly/your-slug"
  3. Confirm your onDynamicLink / onUnifiedLink callback fires with the link data.

See Receive Links → React Native for wiring links to navigation, including cold-start and deferred links.

The Ulinkly CLI inspects your native configuration and flags mismatches against the dashboard.

# macOS / Linux
curl -fsSL https://ulink.ly/install.sh | bash

# Authenticate and link your project
ulink login
cd /path/to/your/react-native-app
ulink project set

# Run verification
ulink verify -v

The CLI detects React Native / Expo projects (it checks package.json for @ulinkly/react-native) and validates the iOS and Android native configuration.

Expo projects: verify after prebuild

For Expo projects, the native ios/ and android/ directories only exist after npx expo prebuild (or npx expo run:*). Run ulink verify once those native folders are present so the CLI can inspect Info.plist, the entitlements file, and AndroidManifest.xml.

8. Confirm analytics

  • In Links, check that click counts increment.
  • Open a link's Analytics page and confirm the platform (iOS or Android) and geo data populate.
Client telemetry

In v0.1.0, traffic from @ulinkly/react-native is reported in analytics as sdk-ios / sdk-android (the same identifiers the native SDKs use). React Native-specific tagging is planned for a future release.

If links aren't resolving, revisit the platform configuration above or see Troubleshoot & Test Deep Links.