Skip to content

klaviyo/klaviyo-expo-plugin

klaviyo-expo-plugin

Contents

Introduction

The Klaviyo Expo plugin is a configuration plugin for Expo that automates the integration of Klaviyo's native SDKs into your Expo project. This plugin handles all the necessary native code modifications required for Klaviyo functionality, eliminating the need for manual native code changes.

The plugin is designed to work with the klaviyo-react-native-sdk and automates the setup of:

  • Push notification open tracking
  • Rich push notification support
  • Badge count management (iOS)
  • Key:value pair data reading
  • Notification service extension setup (iOS)
  • Icon / color notification configuration (Android)
  • Universal links / App links support

Requirements

Expo

  • Your Expo app needs to be run as a development build. This plugin will not work in Expo Go.

Android

  • minSdkVersion of 23+
  • compileSdkVersion of 34+

iOS

  • Minimum Deployment Target 13.0+
  • Apple Push Notification Service (APNs) set up

⚠️ Important Note for Federated Apple Developer Accounts: If you're using a federated Apple Developer account, you'll need to provide an ASC API token with Admin access. While Expo supports federated accounts through ASC API tokens, the EAS CLI cannot directly log into federated accounts for credential management. See Expo's documentation on federated accounts for more details.

Installation

  1. Install the plugin in your Expo project. You need both klaviyo-react-native-sdk and klaviyo-expo-plugin for full functionality:
npm install klaviyo-react-native-sdk
npx expo install klaviyo-expo-plugin
  1. Add the plugin to your app.json or app.config.js:
{
  "expo": {
    "plugins": [
        //... other plugins
      [
        "klaviyo-expo-plugin",
        {
          "android": {
            "logLevel": 1,
            "openTracking": true,
            "notificationIconFilePath": "./your/notification/icon/path.png",
            "notificationColor": "#00FF00"
          },
          "ios": {
            "badgeAutoclearing": true,
            "codeSigningStyle": "Automatic",
            "projectVersion": "1",
            "marketingVersion": "1.0",
            "devTeam": undefined // your devTeam ID here
          }
        }
      ]
    ]
  }
}
  1. Run a prebuild to apply the Expo plugin to your project:
npx expo prebuild
  1. (optional) We recommend using the expo-notifications library for push permissions, token retrieval, and reading push content. Check out our /example project for some ideas on how to use this.

Configuration

Plugin props

Plugin prop Type Required Description
android.logLevel int optional Sets the logging level for the Klaviyo Android SDK. Default: 1 (DEBUG). Values: 0 (NONE), 1 (VERBOSE), 2 (DEBUG), 3 (INFO), 4 (WARNING), 5 (ERROR), 6 (ASSERT)
android.openTracking boolean optional Enables tracking when notifications are opened. Default: true. Note that this is considered to be a dangerous mod, as it directly modifies your MainActivity code.
android.notificationIconFilePath string optional Path to the notification icon file. Should be a white, transparent PNG. Default: none specified. Note that you should set this instead of expo-notifications, as they can conflict with each other.
android.notificationColor string optional Hex color for notification accent. Must be a valid hex value, e.g., "#FF0000" Default: undefined
ios.badgeAutoclearing boolean optional Enables automatic badge count clearing when app is opened. Default: true
ios.codeSigningStyle string optional Declares management style for Code Signing Identity, Entitlements, and Provisioning Profile handled through XCode. Must be either "Manual" or "Automatic". Default: "Automatic". Note: We highly recommend using the automatic signing style. If you select manual, you may need to go into your developer.apple.com console and import the appropriate files and enable capabilities yourself.
ios.projectVersion string optional The internal build number for version. Default: "1"
ios.marketingVersion string optional The app version displayed in the App Store. Must be of the format "X.X" or "X.X.X". Default: "1.0"
ios.devTeam string optional The 10-digit alphanumeric Apple Development Team ID associated with the necessary signing capabilites, provisioning profile, etc. Format: "XXXXXXXXXX" Default: undefined

Note: If you do not need to specify any of these for your project, it will use the defaults defined here. If you do not specify any of these props, you can add the plugin without additional arguments:

{
  "expo": {
    "plugins": [
        //... other plugins
        "klaviyo-expo-plugin"
    ]
  }
}

Debug mode

If you'd like to see debug logs of your prebuild, add the following to your build command:

EXPO_DEBUG=true npx expo prebuild

EXPO_DEBUG=true npx expo run:platform

Please attach these to any build issues you have to help the team debug.

Required config values

In your configuration file, set the following:

Property Details
version Your app version. Corresponds to CFBundleShortVersionString on iOS. Format: "X.X.X" (e.g. "1.0" or "2.3.1")
ios.buildNumber Build number for your iOS app. Corresponds to CFBundleVersion. Format: "42"
ios.bundleIdentifier Bundle identifier for your iOS app. Format: "com.companyname.appname"
ios.infoPlist.UIBackgroundModes set this to ["remote-notification"] to ensure you can receive background push notifications
android.package Package name for your Android app. Format: "com.companyname.appname"

These values are used in various native configuration files and must be properly set for the plugin to work correctly.

In addition, this plugin adds a KlaviyoNotificationServiceExtension target to support Notification Service Extension capabilities on iOS and utilizes app groups to sync and share data across the Klaviyo SDK modules. To support this added target and app group in your Expo configuration file, add:

"appExtensions": [
  {
    "targetName": "KlaviyoNotificationServiceExtension",
    "bundleIdentifier": "{YOUR_BUNDLE_ID}.KlaviyoNotificationServiceExtension",
    "entitlements": {
      "com.apple.security.application-groups": ["group.{YOUR_BUNDLE_ID}.KlaviyoNotificationServiceExtension.shared"]
    }
  }
]

to the extra.eas.build.experimental.ios body of your app config where {YOUR_BUNDLE_ID} is your own app's bundle id. (See our example app config.)

Universal Links

Universal Links (iOS) and App Links (Android) allow you to navigate to a particular page within your app when users tap on links from outside your app, such as from emails, websites, or push notifications. Klaviyo uses universal links for click tracking in email campaigns, allowing you to track user engagement while seamlessly directing them to your app.

The Klaviyo SDK provides the handleUniversalTrackingLink() method to automatically handle click tracking for Klaviyo universal links and redirect users to the intended destination within your app.

⚠️ Important: Universal link tracking requires klaviyo-react-native-sdk version 2.1.0 or higher. Make sure you have the correct version installed:

npm install klaviyo-react-native-sdk@^2.1.0

iOS Setup

To enable universal links on iOS, you need to configure Associated Domains in your app configuration:

  1. Add the associatedDomains array to your ios configuration in app.json:
{
  "expo": {
    "ios": {
      "bundleIdentifier": "com.yourcompany.yourapp",
      "associatedDomains": [
        "applinks:trk.your-domain.com"
      ]
    }
  }
}
  1. Verify domain ownership by hosting an apple-app-site-association file on your domain. This file must be accessible at https://trk.your-domain.com/.well-known/apple-app-site-association or https://trk.your-domain.com/apple-app-site-association.

For more details on setting up Associated Domains, see Apple's documentation.

Android Setup

To enable App Links on Android, you need to configure intent filters in your app configuration:

  1. Add intent filters to your android configuration in app.json:
{
  "expo": {
    "android": {
      "package": "com.yourcompany.yourapp",
      "intentFilters": [
        {
          "action": "VIEW",
          "autoVerify": true,
          "data": [
            {
              "scheme": "https",
              "host": "trk.your-domain.com",
              "pathPrefix": "/u"
            }
          ],
          "category": ["BROWSABLE", "DEFAULT"]
        }
      ]
    }
  }
}
  1. Verify domain ownership by hosting a Digital Asset Links JSON file on your domain. This file must be accessible at https://trk.your-domain.com/.well-known/assetlinks.json.

For more details on setting up Android App Links, see Android's documentation.

React Native Code

In your React Native code, use Expo's expo-linking library to handle both universal links and custom deep links:

import { useEffect } from 'react';
import * as Linking from 'expo-linking';
import { Klaviyo } from 'klaviyo-react-native-sdk';

export default function App() {
  useEffect(() => {
    const handleUrl = (url: string) => {
      // Check if this is a Klaviyo universal tracking link
      if (Klaviyo.handleUniversalTrackingLink(url)) {
        // Klaviyo SDK is handling the tracking link
        // It will track the click and redirect to the destination URL
        console.log('Klaviyo tracking link processed:', url);
        return;
      }

      // Handle other deep links in your app (both custom schemes and universal links)
      // This will be called after Klaviyo redirects from the tracking link to your destination
      console.log('Navigating to destination:', url);
      // Add your navigation logic here (e.g., using React Navigation)
    };

    // Handle the initial URL if the app was opened with a link
    Linking.getInitialURL().then((url) => {
      if (url) {
        handleUrl(url);
      }
    });

    // Listen for deep link events while the app is running
    const subscription = Linking.addEventListener('url', ({ url }) => {
      handleUrl(url);
    });

    return () => {
      subscription.remove();
    };
  }, []);

  // Rest of your app code
  return (
    // Your app components
  );
}

How it works:

  1. When a user taps a Klaviyo tracking link (format: https://trk.your-domain.com/u/...), your app opens
  2. The handleUrl function receives the URL
  3. Klaviyo.handleUniversalTrackingLink() checks if it's a Klaviyo tracking link:
    • If true: The SDK tracks the click event and resolves the tracking link to its destination URL, which triggers another call to handleUrl with the destination
    • If false: The URL is not a Klaviyo tracking link, so you should handle it as a regular deep link
  4. Your app navigates to the appropriate screen based on the destination URL

For more information on deep linking with Expo, see Expo's Linking documentation.

Example app

We created an example app to show how to use this plugin in coordination with the klaviyo-react-native-sdk. Set this up and run based on whichever platform you'd like to test on:

// from repo directory
cd example
npm run clean-android // for android
npm run clean-ios // for ios
npm run reset-all // deletes node modules and built folders

Troubleshooting

Common issues and solutions:

  1. Push Notifications Not Working

    • Verify FCM/APNs setup
    • Check notification permissions
    • Ensure proper configuration in app.json
      • You may need to add the aps-environment to your ios.entitlements in your app config if it is not there already
        ios: {
          entitlements: {
          'aps-environment': 'development', // or 'production'
          // ... other entitlements
          }
        }
        
  2. Deep Links / Universal Links Not Working

    • Custom URL schemes: Verify URL scheme configuration in your app.json
    • Universal Links (iOS):
      • Verify associatedDomains is configured correctly in your app.json
      • Ensure your apple-app-site-association file is properly hosted and accessible
      • Test the domain verification using Apple's app-site-association verification tool
      • Make sure your domain supports HTTPS
    • App Links (Android):
      • Check intent filters are configured with autoVerify: true in your app.json
      • Ensure your assetlinks.json file is properly hosted at https://your-domain.com/.well-known/assetlinks.json
      • Test the domain verification using the command: adb shell pm get-app-links your.package.name
      • Verify the domain supports HTTPS
    • Testing Universal Links:
      • Universal links will NOT work when tapped from within the same app or in simulator under certain conditions
      • Test by sending yourself an email with the link or using the Notes app
      • On Android, use adb shell am start -a android.intent.action.VIEW -d "https://your-tracking-link" to test
  3. Build Errors

    • Clean and rebuild the project
    • Verify Expo SDK version compatibility
    • Check native dependencies
  4. EAS Errors

    • (iOS) Check your Identifiers in the Apple Developer Console and ensure the main app target has Push Notifications and App Group capabilities checked, and your Notification Service Extension target has App Groups capability checked. Check that both App Group capabilities have the app group name with the appended .KlaviyoNotificationServiceExtension.shared suffix
    • (iOS) You should have two different provisioning profiles generated for this project. One for the main target, one for the Notification Service Extension.
    • (iOS) Ensure the correct provisioning profiles are being recognized by EAS, and declare the Notification Service Extension in the extra.eas.build.experimental.ios.appExtensions of your app config as mentioned here

License

The Klaviyo Expo Plugin is available under the terms of the MIT license.

About

Expo support for our klaviyo-react-native SDK

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 8