Documentation
Feedback
Guides
API Reference

Guides
Guides

Installing Activity Flow in React Native apps

In this guide, you'll learn how to install and use the Activity Flow SDK in React Native apps for Android and iOS. By following these steps, you'll be able to track user navigation, order events, deep links, and ad events in your app.

Before you begin

Use React Navigation or Expo Router in your project

To manage navigation between different screens of your app, you must use either React Navigation or Expo Router. This is important because Activity Flow relies on the useFocusEffect hook from React Navigation to detect changes in pages. For more information, refer to the React Navigation guide.

Install the Activity Flow package

Activity Flow depends on @react-native-async-storage/async-storage to store navigation data locally. This ensures events are captured even when the device is offline.

To install the library, follow these steps:

  1. In your project directory, run:

_10
yarn add @vtex/activity-flow @react-native-async-storage/async-storage

or


_10
npm install @vtex/activity-flow @react-native-async-storage/async-storage

  1. For iOS, sync native dependencies:

_10
cd ios && pod install && cd ../

This process installs the Activity Flow script in your app by adding a new dependency in the package.json file.

Instructions

Step 1 - Importing the Activity Flow plugin

In your app's main file (for example, App.js or App.tsx), import the plugin as follows:


_10
import { initActivityFlow } from '@vtex/activity-flow';

Step 2 - Creating an Activity Flow instance

Set the account name to create an instance of the main package class:


_10
function App() {
_10
// Initialize the Activity Flow plugin with your VTEX account name
_10
initActivityFlow({
_10
accountName: 'your-account-name', // Replace with your VTEX account name
_10
});
_10
// ...rest of your app
_10
}

Usage

Tracking page views automatically

To track navigation state changes, integrate the usePageViewObserver hook with the NavigationContainer, the root component of React Navigation, in your app's main file. To do so, follow these steps:

  1. Import the observer hook:


    _10
    import { usePageViewObserver } from '@vtex/activity-flow';

  2. Create a reference for the NavigationContainer:


    _10
    const navigationRef = useRef(null);

  3. Pass the reference to NavigationContainer:


    _10
    <NavigationContainer ref={navigationRef}>
    _10
    {/* Stack configurations */}
    _10
    </NavigationContainer>

  4. Use the observer hook to track page views:


_10
usePageViewObserver({ navigationRef });

Activity Flow now automatically tracks page transitions. When you navigate between screens, it records each page view and sends event data.

Tracking order group

The Activity Flow SDK can automatically capture order details from navigation parameters, such as orderGroup or orderPlaced, and any query parameters from the navigator stack. These parameters help validate checkout events and confirm successful purchases.

To enable this feature, include the required parameter in your page redirect configuration. See the example below:

//checkout_screen.tsx

_11
<TouchableOpacity
_11
onPress={() =>
_11
navigation.navigate('/purchase_success', {
_11
orderGroup: '1234567890',
_11
orderId: '1234',
_11
// ...other params
_11
})
_11
}
_11
>
_11
<Text>Confirm Purchase</Text>
_11
</TouchableOpacity>

When a user completes a purchase, Activity Flow automatically captures orderGroup and other query parameters during the screen transition.

The Activity Flow SDK automatically captures deep-link query parameters from the usePageViewObserver hook and includes them in page view events. To enable this feature, configure deep linking in your app according to its platform: Android or iOS.

Android

To enable deep-link handling in your Android app, add intent filters to the AndroidManifest.xml file for each route that can be accessed via a deep link. See the example below:

AndroidManifest.xml

_18
_18
<intent-filter>
_18
<action android:name="android.intent.action.VIEW" />
_18
<category android:name="android.intent.category.DEFAULT" />
_18
<category android:name="android.intent.category.BROWSABLE" />
_18
<data
_18
android:scheme="https"
_18
android:host="mystore.com"
_18
android:pathPrefix="{APP_ROUTE}"
_18
/>
_18
</intent-filter>
_18
_18
<intent-filter>
_18
<action android:name="android.intent.action.VIEW" />
_18
<category android:name="android.intent.category.DEFAULT" />
_18
<category android:name="android.intent.category.BROWSABLE" />
_18
<data android:scheme="{YOUR_CUSTOM_SCHEME}" />
_18
</intent-filter>

This AndroidManifest adds two intent filters for deep linking:

  • One for HTTPS URLs starting with "https://example.com/{APP_ROUTE}".
  • One for a custom scheme "{YOUR_CUSTOM_SCHEME}". Both use action.VIEW, category_DEFAULT, and category_BROWSABLE, allowing the Activity Flow to launch from browsers or other apps. The data tag for deep link via HTTP specifies the scheme, host, and an optional pathPrefix, matching any path that begins with that prefix (for example, "/products/42" if {APP_ROUTE} is "/products"). The main difference between intent filters for different routes is the android:pathPrefix attribute, which specifies the app route. For a deep link via a custom scheme, the data tag sets the scheme, matching any URL that starts with that scheme (for example, myapp://... if {YOUR_CUSTOM_SCHEME} is myapp).

iOS

To enable deep-link handling in your iOS app, add your URL scheme to the Info.plist file and handle incoming URLs in the AppDelegate file:

  1. Register your custom URL scheme by adding the following configuration to your Info.plist file:
info.plist

_11
<key>CFBundleURLTypes</key>
_11
<array>
_11
<dict>
_11
<key>CFBundleURLSchemes</key>
_11
<array>
_11
<string>{YOUR_BUNDLE_URL_SCHEME}</string>
_11
</array>
_11
<key>CFBundleURLName</key>
_11
<string>{YOUR_BUNDLE_URL_NAME}</string>
_11
</dict>
_11
</array>

In the info.plist configuration, {YOUR_BUNDLE_URL_SCHEME} is the custom URL scheme your app will handle, that is, the prefix before :// in deep links. For example, setting it to myapp makes links like myapp://path open your app. Enter only the scheme name, without ://. The {YOUR_BUNDLE_URL_NAME} label is a unique identifier for this URL type entry, typically in reverse-DNS format, used to distinguish the configuration and doesn't affect routing. For example, com.example.appname.

  1. Handle incoming deep links by modifying your AppDelegate.swift (or AppDelegate.mm) file. The following example handles deep links for cold starts, when the app isn't running, and warm starts, when the app is already running.
AppDelegate.swift

_64
import UIKit
_64
import React
_64
_64
@UIApplicationMain
_64
class AppDelegate: UIResponder, UIApplicationDelegate {
_64
func application(
_64
_ application: UIApplication,
_64
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
_64
) -> Bool {
_64
// ... your existing setup code ...
_64
// Capture initial URL if app was launched with a deep link (cold start)
_64
if let initialURL = launchOptions?[UIApplication.LaunchOptionsKey.url] as? URL {
_64
NotificationCenter.default.post(
_64
name: NSNotification.Name("RCTOpenURLNotification"),
_64
object: nil,
_64
userInfo: ["url": initialURL]
_64
)
_64
}
_64
return true
_64
}
_64
_64
* Handles incoming URLs from Custom URL Schemes (e.g., myapp://path)
_64
func application(
_64
_ app: UIApplication,
_64
open url: URL,
_64
options: [UIApplication.OpenURLOptionsKey : Any] = [:]
_64
) -> Bool {
_64
// Post notification for Activity Flow's CaptureDeepLinkModule
_64
NotificationCenter.default.post(
_64
name: NSNotification.Name("RCTOpenURLNotification"),
_64
object: nil,
_64
userInfo: ["url": url]
_64
)
_64
// Pass the URL to React Native's standard linking manager
_64
return RCTLinkingManager.application(app, open: url, options: options)
_64
}
_64
_64
* Handles incoming URLs from Universal Links (e.g., https://mystore.com/product)
_64
func application(
_64
_ application: UIApplication,
_64
continue userActivity: NSUserActivity,
_64
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
_64
) -> Bool {
_64
// Check if the activity is a web browsing activity (Universal Link)
_64
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
_64
// Post notification for Activity Flow's CaptureDeepLinkModule
_64
if let url = userActivity.webpageURL {
_64
NotificationCenter.default.post(
_64
name: NSNotification.Name("RCTOpenURLNotification"),
_64
object: nil,
_64
userInfo: ["url": url]
_64
)
_64
}
_64
}
_64
// Pass the user activity to React Native's standard linking manager
_64
return RCTLinkingManager.application(
_64
application,
_64
continue: userActivity,
_64
restorationHandler: restorationHandler
_64
)
_64
}
_64
_64
return false
_64
}

Your Android app can now be launched from both web links (https://mystore.com/products/42) and custom scheme links: (myapp://products/42).

Tracking ad events

Ad tracking is available only for accounts that use VTEX Ads. If you're interested in this feature, open a ticket with VTEX Support.

To configure ad tracking, follow these steps:

  1. Create the ad object

To track events, provide an object containing all information relevant to the ad event. The accountName field is mandatory.


_10
const adParams = {
_10
accountName: 'your-account-name',
_10
adId: 'main-banner-01',
_10
// ...other parameters
_10
};

  1. Integrate the ad tracker into your components

There are two methods to integrate the ad tracker into your components: Using the AFAdsTracker Wrapper component or using the useAdsTracker hook.

Using the AFAdsTracker wrapper component

To track ad events with the AFAdsTracker, wrap your ad component with it and provide the necessary tracking parameters. The wrapper will automatically manage all required event handlers.

Use this method when your entire child component represents a clickable ad. See the example below:


_10
<AFAdsTracker params={adParams}>
_10
<TouchableOpacity
_10
style={styles.button}
_10
onPress={() => {
_10
navigation.navigate('/checkout');
_10
}}
_10
>
_10
<Text style={styles.buttonText}>Buy</Text>
_10
</TouchableOpacity>
_10
</AFAdsTracker>

The example below shows a React Native app that uses @vtex/activity-flow with React Navigation to track navigation across multiple screens and ad events from a single location.


_97
// App.tsx
_97
import React, { useRef, useEffect } from 'react';
_97
import { View, Text, Button, TouchableOpacity, StyleSheet } from 'react-native';
_97
import { NavigationContainer } from '@react-navigation/native';
_97
import { createNativeStackNavigator } from '@react-navigation/native-stack';
_97
import {
_97
initActivityFlow,
_97
usePageViewObserver,
_97
AFAdsTracker,
_97
} from '@vtex/activity-flow';
_97
_97
type NavProps = { navigation: any };
_97
_97
const HomeScreen = ({ navigation }: NavProps) => {
_97
return (
_97
<View>
_97
<Text>Home</Text>
_97
<Button title="Go to Profile" onPress={() => navigation.navigate('Profile')} />
_97
<View />
_97
<Button title="Go to Ads" onPress={() => navigation.navigate('Ads')} />
_97
</View>
_97
);
_97
};
_97
_97
const ProfileScreen = ({ navigation }: NavProps) => (
_97
<View>
_97
<Text>Profile</Text>
_97
<Button title="Go to Details" onPress={() => navigation.navigate('Details')} />
_97
</View>
_97
);
_97
_97
const DetailsScreen = ({ navigation }: NavProps) => {
_97
return (
_97
<View>
_97
<Text>Details</Text>
_97
<Button title="Go Back" onPress={() => navigation.goBack()} />
_97
</View>
_97
);
_97
};
_97
_97
const AdsScreen = () => {
_97
// Include your VTEX accountName here (required for sending ad events)
_97
const adParams = {
_97
accountName: 'your-account-name',
_97
adId: '12345',
_97
campaign: 'summer_campaign',
_97
};
_97
_97
return (
_97
<View>
_97
<Text>Ads</Text>
_97
{/* Wrap your ad with AFAdsTracker. The wrapper wires impression/view/click. */}
_97
<AFAdsTracker params={adParams}>
_97
<TouchableOpacity
_97
onPress={() => {
_97
// Your CTA action goes here (e.g., navigate, open link)
_97
console.log('Ad clicked — navigate or handle CTA');
_97
}}
_97
>
_97
<Text>Sponsored Ad</Text>
_97
<Text>Check out our new campaign!</Text>
_97
<Text>Buy now</Text>
_97
</TouchableOpacity>
_97
</AFAdsTracker>
_97
</View>
_97
);
_97
};
_97
_97
const Stack = createNativeStackNavigator();
_97
_97
export default function App() {
_97
const navigationRef = useRef(null);
_97
_97
// Initialize Activity Flow once with your VTEX account name
_97
useEffect(() => {
_97
initActivityFlow({
_97
accountName: 'your-account-name',
_97
});
_97
}, []);
_97
_97
// Track page views automatically (attach to the navigation ref)
_97
usePageViewObserver({ navigationRef });
_97
_97
return (
_97
<NavigationContainer ref={navigationRef as any}>
_97
<Stack.Navigator initialRouteName="Home">
_97
<Stack.Screen name="Home" component={HomeScreen} />
_97
_97
<Stack.Screen name="Profile" component={ProfileScreen} />
_97
_97
<Stack.Screen name="Details" component={DetailsScreen} />
_97
_97
<Stack.Screen name="Ads" component={AdsScreen} />
_97
</Stack.Navigator>
_97
</NavigationContainer>
_97
);
_97
}

The app initializes tracking via initActivityFlow with your VTEX account name, and enables automatic page view tracking by calling usePageViewObserver with a NavigationContainer ref.

On the Ads screen, ad parameters (including the required accountName) are passed to the AFAdsTracker wrapper, which wraps a fully clickable child to automatically fire impression, view, and click events without manual handler wiring.

The wrapper detects clicks via onTouchEndCapture. If your ad interaction isn't based on it or can't be wrapped, use the useAdsTracker hook instead.

To use this example in your project, remember to replace accountName based on your scenario.

Using the useAdsTracker hook

The useAdsTracker hook provides more control over how events are attached. It returns handlers and a ref that you must apply manually to your components.

Use this method when:

  • You can't use an extra <View> to wrap your component.
  • The component that receives events (for example, a TouchableOpacity) is different from the component that defines the visibility area.
  • You integrate with third-party component libraries that don’t allow a generic wrapper.
  • The click property isn't onTouchEnd, since the AFAdsTracker wrapper uses onTouchEndCapture.

Follow the steps below to use this hook:

  1. Import and call the hook:

_10
import { useAdsTracker } from '@vtex/activity-flow';
_10
_10
const { handleLayout, handlePress, viewRef } = useAdsTracker(adParams);

  1. Attach the handlers and ref to your component. See the example below:

_10
<TouchableOpacity
_10
ref={viewRef} // Attach the ref to track visibility (view)
_10
onLayout={handleLayout} // Attach the layout handler (impression)
_10
onPress={handlePress} // Attach the click handler (click)
_10
>
_10
<Image source={{}} />
_10
</TouchableOpacity>

The hook returns an object with three properties:

  • handleLayout: A function to be passed to your component's onLayout prop. It fires the impression event.
  • handlePress: A function to be passed to a touch prop, like onPress or onTouchEndCapture. It fires the click event.
  • viewRef: A ref that must be attached to the ref prop of the ad's main component. It monitors visibility and fires the view event.

Below is an implementation example of a React Native code that defines a CustomAd component. This component uses the useAdsTracker hook from the @vtex/activity-flow library to integrate with an ad tracking system for visibility and interaction.


_24
import { useAdsTracker } from "@vtex/activity-flow";
_24
import { Image, TouchableOpacity } from "react-native";
_24
_24
export const CustomAd = () => {
_24
// 1. Define the parameters
_24
const adParams = {
_24
accountName: 'my-company-x',
_24
adId: 'sidebar-banner-02',
_24
};
_24
_24
// 2. Call the hook to get the handlers and the ref
_24
const { handleLayout, handlePress, viewRef } = useAdsTracker(adParams);
_24
_24
return (
_24
// 3. Attach the handlers and ref to your component
_24
<TouchableOpacity
_24
ref={viewRef} // Attach the ref to track visibility (view)
_24
onLayout={handleLayout} // Attach the layout handler (impression)
_24
onPress={handlePress} // Attach the click handler (click)
_24
>
_24
<Image source={{}} />
_24
</TouchableOpacity>
_24
);
_24
};

The component's purpose is to render an ad inside a touchable container and report three key events to the tracking system: impression, view, and click.

Contributors
1
Photo of the contributor
Was this helpful?
Yes
No
Suggest Edits (GitHub)
Contributors
1
Photo of the contributor
Was this helpful?
Suggest edits (GitHub)
On this page