Skip to content

Expo SDK ​

Linkrunner MMP SDK for Expo: install attribution, deferred deep linking, in-app events and revenue tracking.

Prefer to let your AI coding agent do this? Install the Linkrunner skill and Claude Code, Cursor, GitHub Copilot, or Windsurf will wire up the SDK and deep links for you:

bash
npx @linkrunner/skills add expo

Then ask your agent to "integrate Linkrunner". See Linkrunner Agent Skills.

This guide will help you install, configure, and use the Linkrunner SDK in your Expo application.

Migrating from an older version? For instructions on upgrading from 1.x.x/2.x.x to version 3.x.x, see the Migration Guide below.

Requirements ​

  • Expo SDK 52.0 or higher
  • Node.js 18.0 or higher
  • React Native 0.70 or higher
  • rn-linkrunner 4.0.0 or higher when you use expo-linkrunner 4.0.0 or higher

Installation ​

Step 1: Install Both Packages ​

Install both the React Native and Expo packages:

bash
npm install rn-linkrunner

npx expo install expo-linkrunner

Step 2: Add Plugin to app.json ​

Important: You must add the expo-linkrunner plugin to your app.json plugins array:

json
{
    "expo": {
        "plugins": [
            [
                "expo-linkrunner",
                {
                    "userTrackingPermission": "This identifier will be used to deliver personalized ads.",
                    "debug": true
                }
            ]
        ]
    }
}

What the expo-linkrunner Plugin Does ​

When you add expo-linkrunner to your app.json plugins and run the Expo prebuild step, the config plugin automatically makes the following changes to your iOS project:

  • Adds NSUserTrackingUsageDescription in your iOS Info.plist if not already present (skipped when disableIdfa is set).
  • Adds the SKAdNetwork postback keys NSAdvertisingAttributionReportEndpoint and AttributionCopyEndpoint (both set to https://linkrunner-skan.com) in your iOS Info.plist if not already present, so Apple sends SKAN postback copies to Linkrunner.

These changes are required for IDFA (Identifier for Advertisers) tracking, Apple's App Tracking Transparency (ATT) compliance, and SKAN attribution. For complete SKAdNetwork integration details, see the SKAdNetwork Integration Guide.

Step 3: Prebuild Your Project ​

The expo-linkrunner package is a config plugin that automatically configures your Expo project for Linkrunner SDK integration. Once you've completed the installation and configuration steps above:

  • For EAS Build: If your android and ios folders are in .gitignore (recommended), EAS Build will automatically run the prebuild step during the build process. No manual action required.

  • For local development or custom development clients:

    bash
    npx expo prebuild

Step 4: Android Backup Configuration ​

For Android apps, the SDK provides backup rules to exclude Shared Preferences data from backup. This prevents the retention of the Linkrunner install ID during reinstallation, ensuring accurate detection of new installs and re-installs.

For detailed backup configuration instructions, please refer to the Android SDK Backup Configuration.

Google Integrated Conversion Measurement (Optional) ​

Integrated Conversion Measurement (ICM) recovers Google App Campaign installs on iOS that Google cannot attribute because there is no click identifier and no IDFA to match on. Google's On-Device Measurement (ODM) SDK turns the click context into an encrypted signal that never leaves the device, and Linkrunner sends it with the install. See Google ICM for how it works.

Set this up if you run Google App Campaigns for your iOS app. Requires rn-linkrunner 3.1.0 or later.

Google keeps ODM inactive for users in the European Economic Area, the United Kingdom, and Switzerland, so ICM recovers nothing for that traffic. Elsewhere, Google reports improved coverage for iOS 14+ users.

ICM also needs an iOS link ID configured in your Google Ads integration. Google has nowhere to send the conversion without one. See Prerequisites.

Add Google's On-Device Measurement SDK to the iOS build

Already using the Firebase iOS SDK 11.14.0 or later? The FirebaseAnalytics pod brings this SDK in for you. Skip this step.

rn-linkrunner does not bundle this SDK, so apps that skip ICM carry none of its weight. Expo generates the Podfile, so add the pod through expo-build-properties:

bash
npx expo install expo-build-properties
json
{
    "expo": {
        "plugins": [
            [
                "expo-build-properties",
                {
                    "ios": {
                        "extraPods": [{ "name": "GoogleAdsOnDeviceConversion" }]
                    }
                }
            ]
        ]
    }
}

CocoaPods adds the -ObjC and -lc++ linker flags for you, so there are no Xcode Build Settings to change.

If you manage ios/ yourself instead of generating it, add pod 'GoogleAdsOnDeviceConversion' to ios/Podfile and run pod install.

Rebuild your development client

bash
npx expo prebuild --clean

ICM needs native code, so it does not work in Expo Go. Build a development client or run an EAS build.

Report consent

Set the values with setConsent before you call init:

javascript
import linkrunner from "rn-linkrunner";

linkrunner.setConsent({
    isEEA: "granted",
    hasConsentForDataUsage: "granted",
    hasConsentForAdsPersonalization: "denied",
});

await linkrunner.init("YOUR_PROJECT_TOKEN");

See Google Integrated Conversion Measurement in the React Native guide for what each signal means and how to verify the setup. For users outside the EEA, the UK, and Switzerland, report isEEA as denied and leave the other two unset. See Send Consent.

SDK Usage ​

The expo-linkrunner plugin handles the native configuration automatically. All SDK methods and APIs are provided by the rn-linkrunner package, which is why you'll use the React Native documentation for implementation details.

Since expo-linkrunner is a config plugin that prepares your project, the actual SDK usage is identical to React Native. Please refer to the React Native SDK Guide for:

  • Initializing the SDK
  • User registration
  • Handling deep links with handleDeeplink
  • Getting attribution data
  • Setting user data
  • Tracking custom events
  • Revenue tracking
  • Ecommerce Events
  • Enhanced privacy controls

capturePayment and removePayment are deprecated in rn-linkrunner 4.0.0. They still work and show a deprecation warning, and they will be removed in a future major version. With expo-linkrunner 4.0.0, send a payment as an in-app event: call trackEvent with the old payment type as the event name, and put amount, paymentId, currency, and userId in the event data. Keep the old type spelling exactly, such as FIRST_PAYMENT, so your existing mappings keep working. Any other name needs a new mapping in the dashboard. See Migrate payments to in-app events. For status, examples, and how payments reach ad networks, see Track payments.

Tracking Payments ​

Expo apps call trackEvent from rn-linkrunner. When the event data has amount, paymentId, and currency, the event is a payment. For the full key table and status values, see Tracking Payments in the React Native guide.

javascript
import linkrunner, { PaymentStatus } from "rn-linkrunner";

const trackPurchase = async () => {
  try {
    await linkrunner.trackEvent(
      "purchase", // Payment name, stored as the payment type in reports and mappings
      {
        amount: 1499,                            // Required for a payment: total the user paid, a finite number >= 0
        paymentId: "order_123",                  // Required for a payment: your order or transaction ID, used to deduplicate
        currency: "INR",                         // Required for a payment: 3-letter ISO 4217 code
        userId: "user_123",                      // Required for a payment: your user ID. "" uses the user saved by signup or setUserData
        status: PaymentStatus.PAYMENT_COMPLETED, // Optional: PAYMENT_INITIATED, PAYMENT_COMPLETED (default), PAYMENT_FAILED, or PAYMENT_CANCELLED
        // Optional: any other keys stay as event data. Meta and Google read these product fields
        content_ids: ["sku_1", "sku_2"],
        contents: [
          { id: "sku_1", quantity: 1, item_price: 999 },
          { id: "sku_2", quantity: 1, item_price: 500 },
        ],
        content_type: "product",
        num_items: 2,
        order_id: "order_123",
      },
    );
  } catch (error) {
    // On Android an invalid payment throws here, and nothing is sent
    console.error("Error tracking payment:", error);
  }
};

Only PAYMENT_COMPLETED counts as revenue and is sent to ad networks. Plain strings such as "PAYMENT_COMPLETED" also work for status.

Function Placement Guide ​

Expo uses the rn-linkrunner runtime. The function links below open the implementation details in the React Native guide.

FunctionRequirementWhere to PlaceWhen to Call
linkrunner.initRequiredApp.tsx within useEffectOnce when app starts
linkrunner.signupRequiredIdentification flow (signup or login)Once when the user is identified
linkrunner.setCustomerUserIdRequiredAfter init, when your user ID is availableAs early as the user's ID is available
linkrunner.handleDeeplinkRequiredDeep link entry pointsWhen app is opened via a deep link
linkrunner.getAttributionDataOptionalAttribution data handling flowWhenever the attribution data is needed
linkrunner.setAdditionalDataOptionalIntegration codeWhen third-party integration IDs are available
linkrunner.setUserDataOptionalApp start, authentication or profile update logicRight after init with your analytics IDs, and when user details become available
linkrunner.trackEventOptionalThroughout app, including ecommerce flowsWhen specific user actions occur, including AddToCart or ViewContent events
linkrunner.trackEvent with payment keysOptionalPayment processing, including ecommerce purchasesWhen user makes a payment, including Purchase events with ecommerce data
linkrunner.setPushTokenOptionalPush notification setupWhen FCM/APNs token is available
linkrunner.setConsentOptionalApp initialization or consent flowBefore init, and again when consent changes
linkrunner.enablePIIHashingOptionalPrivacy configurationWhen enabling PII hashing
linkrunner.enableTCFConsentCollectionOptionaliOS consent configurationOn every launch before init, if you opt into TCF consent collection
linkrunner.getPackageVersionOptionalDiagnosticsWhen you need the installed SDK package version

Migration Guide ​

If you are using expo-linkrunner with v1.x.x or v2.x.x then moving forward you'll need to migrate to a v3.x.x.

Architecture Changes ​

Starting with version 3.0.0, the Expo SDK has been redesigned to use a new architecture:

  • Config Plugin Architecture: The SDK now uses an Expo config plugin for better integration with the Expo ecosystem
  • React Native SDK: Business logic is now handled by the React Native SDK, ensuring consistency across platforms
  • Improved Developer Experience: Simplified setup with automatic native configuration

Breaking Changes ​

  • The package installation process has changed to require both React Native and Expo SDKs
  • Plugin configuration is now required in app.json
  • All SDK methods and APIs are now provided by the rn-linkrunner package

Migration Steps ​

  1. Uninstall the old package:

    bash
    npx expo uninstall expo-linkrunner
  2. Install the new version:

    Follow the Installation steps above to install both rn-linkrunner and expo-linkrunner.

  3. Update your code to use rn-linkrunner:

    The expo-linkrunner package is now just a config plugin. All SDK methods and APIs are now provided by the rn-linkrunner package. You'll need to update your imports and usage accordingly.

    For detailed implementation instructions, see the SDK Usage section above.

Publishing to Google Play or the App Store? See Data the SDK collects for what the SDK sends and the answers for the Play Data safety form and Apple's App Privacy questions.

Next Steps ​

Support ​

If you encounter issues during integration, contact us at support@linkrunner.io.