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.
@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
Expo (managed workflow / dev client / prebuild) — recommended
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
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.
Path A — Expo Config Plugin (recommended)
Add the plugin to your app.json (or app.config.js) under expo.plugins:
{
"expo": {
"plugins": [
["@ulinkly/react-native", {
"scheme": "myapp",
"domains": ["myapp.shared.ly"]
}]
]
}
}
| Prop | Type | Required | Description |
|---|---|---|---|
scheme | string | Yes | Your app's custom URL scheme (without ://). Must match what you register in the Ulinkly dashboard. |
domains | string[] | No | One 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
CFBundleURLTypesentry forschemeinInfo.plist; addsapplinks:<domain>entries to the Associated Domains entitlement. - Android — Adds a custom-scheme
<intent-filter>to your main activity; adds anandroid:autoVerify="true"HTTPS host<intent-filter>for each domain.
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 → + Capability → Associated 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
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
myappwith your registered URL scheme. - Replace
myapp.shared.lywith the domain you configured in the Ulinkly dashboard. - Ulinkly automatically serves the
.well-known/assetlinks.jsonandapple-app-site-associationfiles 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().
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 />;
}
initialize()is asynchronous — alwaysawaitit.- On Android, any method called before
initialize()resolves is rejected with aULinkError. - On iOS, pre-init calls are queued, but order is undefined —
awaitinit 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.bundleIdentifierfromapp.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.packagefromapp.json. - Bare RN: the
namespaceinandroid/app/build.gradle(orpackageinAndroidManifest.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
- Go to the Domains section.
- Add your domain if needed (Shared
.shared.lysubdomain, or a custom domain with DNS verification). - Ensure the domain matches the
domainsvalue in your config plugin (Expo) or your Associated Domains / intent-filter host (bare RN).
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.
6. Create a test link
- Links → Create Link in the dashboard.
- Choose Unified (store redirect) or Dynamic (deep link parameters).
- Save and copy the short URL.
You can also create links programmatically — see Create Dynamic Links → React Native.
7. Test on devices
- Build and run a development build on each platform:
(For bare RN:
npx expo run:ios # or npx expo run:androidnpx react-native run-ios/run-android.) - 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" - Confirm your
onDynamicLink/onUnifiedLinkcallback fires with the link data.
See Receive Links → React Native for wiring links to navigation, including cold-start and deferred links.
7.5 Verify with Ulinkly CLI (Recommended)
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.
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.
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.