The React Native SDK makes it easy to track events in React Native applications. The SDK supports both iOS and Android applications.
Installation
The React Native SDK is distributed via npm.
Install the required dependencies into your React Native project via npm:
npm install --save \
-sdks/events-sdk-react-native \
-sdks/sovran-react-native \
react-native-get-random-values \
-native-async-storage/async-storage
iOS
On iOS, you will also need to install the native modules by running:
npx pod-install
Android
The SDK requires additional permissions on Android in order to populate the device context for events.
Add these permissions to your app by updating your AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
SDK Initialization
Once the dependencies are installed, you can import the tracking client into your application.
import { createClient } from '@ht-sdks/events-sdk-react-native';
const htClient = createClient({
writeKey: 'API_KEY'
});
The client can either be passed around explicitly throughout your app, or
exposed through a React context
hook. To pass the
context through a Context, use AnalyticsProvider and useAnalytics.
import { AnalyticsProvider } from '@ht-sdks/events-sdk-react-native';
// The Child component can retrieve the htClient by calling `useAnalytics()`.
const App = () => (
<AnalyticsProvider client={htClient}>
<Child />
</AnalyticsProvider>
);
The client may be configured with the following options:
| Parameter | Type | Description |
|---|---|---|
writeKey | String | The write key used to identify the source in Hightouch. |
collectDeviceId | Boolean | Whether to autocollect a device ID via the Android DRM API. The ID is stored in context.device.id on all events. This is only supported on Android, and is disabled by default. |
debug | Boolean | Whether to generate debug logs. Defaults to true. |
logger | Logger | Custom logger instance to redirect internal logging from the library. |
flushAt | Number | Maximum number of events to accumulate before sending to the Hightouch API. Defaults to 20. |
flushInterval | Number | Maximum number of seconds to hold events before sending to the Hightouch API. Defaults to 30. |
flushPolicies | Array | Optional advanced customization for how events are buffered before sending to the Hightouch API. Mutually exclusive with flushAt and flushInterval — if set, those properties are ignored. |
maxBatchSize | Number | The maximum number of events to send in one HTTP request to the Hightouch API. You may want to adjust this if your events are extremely large. Defaults to 1000. |
trackAppLifecycleEvents | Boolean | Whether to automatically track lifecycle events like Application Installed. Defaults to false. |
trackDeepLinks | Boolean | Whether to automatically track deep links. Only supported for Android. Defaults to false. |
proxy | String | Used to override the URL used for sending events to Hightouch. This should contain the full path, e.g. https://us-east-1.hightouch-events.com/v1/batch |
Manual tracking API
Identify
The identify method is used to send an identify event.
If identify is called multiple times for the same user, the traits are
merged together.
Method signature:
htClient.identify(userId, [traits])
Method parameters:
| Parameter | Type | Description |
|---|---|---|
userId | String | The user's persistent ID. |
traits | Object | Additional traits about the user, such as email and name. |
Track
The track method is used to send a track event.
Method signature:
htClient.track(event, [properties])
Method parameters:
| Parameter | Type | Description |
|---|---|---|
event | String | The event name (for example "Checked Out"). |
properties | Object | Additional properties about the event, such as product_id. |
Screen
The screen method is used to send a screen event.
Method signature:
htClient.screen(name, [properties])
Method parameters:
| Parameter | Type | Description |
|---|---|---|
name | String | The screen's name. For example, "Getting started". |
properties | Object | Additional properties about the event. |
Group
The group method sends a group event.
Method signature:
htClient.group(groupId, [traits])
Method parameters:
| Parameter | Type | Description |
|---|---|---|
groupId | String | The id for the group. |
traits | Object | Additional traits about the group, such as company_name. |
Reset
The reset method resets the identify calls for the local session.
Specifically, it resets the anonymousId, userId, and traits.
The reset method should be called when users log out. This way, if the user
logs back in with another account, the userIds and traits from the different
sessions remain separate.
Method signature:
htClient.reset()
Flush
The flush method forces the SDK to send all queued events to the Hightouch API immediately, rather than waiting for the next automatic flush cycle.
Method signature:
await htClient.flush()
Call flush after mission-critical events like purchases or post-login identify calls to make sure those events are delivered before the user closes or backgrounds the app.
// Ensure a purchase event is sent immediately
htClient.track("Order Completed", { orderId: "12345", total: 59.99 });
await htClient.flush();
Flush behavior
The SDK queues events locally and sends them to the Hightouch API in batches. By default, the SDK flushes when either of these conditions is met:
- Count threshold (
flushAt): The queue accumulates 20 events (configurable via theflushAtoption increateClient). - Time interval (
flushInterval): 30 seconds have elapsed since the last flush (configurable via theflushIntervaloption increateClient).
Events on the queue are persisted to AsyncStorage, so they survive app restarts. Only events that are successfully delivered are removed from the queue. Failed events are retried on the next flush cycle.
Flush policies
For more control over when batches are sent, you can pass custom flush policies via the flushPolicies option in createClient. The SDK ships with the following built-in policies:
CountFlushPolicy(limit)— flushes after a specified number of events accumulate. This is the policy behind theflushAtdefault.TimerFlushPolicy(intervalMs)— flushes on a recurring time interval (in milliseconds). This is the policy behind theflushIntervaldefault.BackgroundFlushPolicy()— flushes automatically when the app is sent to the background. This is useful for ensuring events are sent before the OS suspends your app.StartupFlushPolicy()— triggers a single flush immediately when the client starts. This is useful for sending any events that were queued from a previous session.
import {
createClient,
CountFlushPolicy,
TimerFlushPolicy,
BackgroundFlushPolicy,
StartupFlushPolicy,
} from "@ht-sdks/events-sdk-react-native";
const htClient = createClient({
writeKey: "API_KEY",
flushPolicies: [
new CountFlushPolicy(10), // Flush every 10 events
new TimerFlushPolicy(15 * 1000), // Flush every 15 seconds
new BackgroundFlushPolicy(), // Flush when the app is backgrounded
new StartupFlushPolicy(), // Flush any queued events on startup
],
});
flushPolicies is mutually exclusive with flushAt and flushInterval. If
you provide flushPolicies, the SDK ignores flushAt and flushInterval
entirely and uses only the policies you specify. The default count and timer
policies are not included automatically, so add them explicitly if you still
want that behavior.
You can also add or remove flush policies at runtime:
const backgroundPolicy = new BackgroundFlushPolicy();
// Add a policy after initialization
htClient.addFlushPolicy(backgroundPolicy);
// Remove a policy later
htClient.removeFlushPolicy(backgroundPolicy);
Anonymous ID management
Every device that uses the SDK is assigned an anonymousId — a UUID that the SDK auto-generates on first launch and persists in AsyncStorage. The anonymousId is attached to every event, allowing Hightouch to stitch anonymous activity to a known user once identify is called.
The anonymous ID and other user info are accessible through the userInfo property on the client.
Get the anonymous ID
To read the current anonymousId, call userInfo.get(). Pass true for a safe async read that waits for storage to be ready:
// Async (recommended) — waits for storage to be fully restored
const { anonymousId } = await htClient.userInfo.get(true);
// Sync — returns the current in-memory value immediately
const { anonymousId } = htClient.userInfo.get();
This is useful when you need to pass the anonymous ID to another system or include it in a deep link for cross-platform identity resolution.
Set or override the anonymous ID
To replace the auto-generated anonymousId with your own value, use userInfo.set():
await htClient.userInfo.set((state) => ({
...state,
anonymousId: "my-custom-anonymous-id",
}));
Common use cases for overriding the anonymous ID include:
- Migrating from another vendor. If your app previously used a different analytics SDK, you can carry over the existing anonymous ID so that pre- and post-migration events are attributed to the same device.
- Using your own device identifier. If your app already generates a stable device ID, you can use it as the anonymous ID to simplify identity resolution.
Set the anonymous ID before any identify or track calls so that all events
share the same identifier from the start of the session.
Relationship between anonymousId and userId
Before identify is called, all events carry only the anonymousId. Once you call identify(userId), subsequent events carry both the anonymousId and the userId. Hightouch uses both values to stitch anonymous and identified activity together during identity resolution.
Calling reset() generates a new anonymousId and clears the userId and traits, starting a fresh anonymous session. If you want to keep the current anonymousId while clearing the user, pass false:
// Reset everything including anonymousId (default)
await htClient.reset();
// Clear userId and traits but keep the current anonymousId
await htClient.reset(false);
Out of the box events
Lifecycle events
When trackAppLifecycleEvents is enabled, Hightouch automatically tracks the following events:
Application Installed-- Emitted when the app is first opened after a new install.Application Updated-- Emitted when the app is first opened after upgrading from a previous version.Application Opened-- Emitted whenever the app is opened (including when it's resuming from the background).Application Backgrounded-- Emitted whenever the app is backgrounded.
Advertising identifiers
To collect IDFAs and AAIDs for advertising identification, you may use the
@ht-sdks/events-sdk-react-native-plugin-advertising-id and
@ht-sdks/events-sdk-react-native-plugin-idfa packages.
These plugins load native modules that pull the local advertising ID.
Android AAID
Once setup, the AAID will be stored in context.device.advertisingId
-
Add the Android AAID plugin to your project.
npm install -sdks/events-sdk-react-native-plugin-advertising-id -
Add the plugin to your code by adding the following code after initializing your client.
htClient.add({plugin: new AdvertisingIdPlugin()}); -
Update your Android application manifest to include your AdMob app ID.
<manifest> <application> <!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 --> <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy"/> </application> </manifest>
iOS IDFA
Once setup, the IDFA will be stored in context.device.advertisingId
-
Add the iOS IDFA plugin to your project.
npm install -sdks/events-sdk-react-native-plugin-idfa -
Add the plugin to your code by adding the following code after initializing your client.
htClient.add({plugin: new IdfaPlugin()}); -
Update your
Info.plistto set NSUserTrackingUsageDescription. This description will be displayed when prompting users for permission to track their IDFA.
Tips and troubleshooting
Missing events
If events are not appearing in Hightouch, work through the following steps:
-
Upgrade to the latest SDK version. Run
npm install @ht-sdks/events-sdk-react-native@latestto pick up the most recent bug fixes and delivery improvements. -
Add a manual
flushafter critical events. The SDK buffers events for up to 30 seconds by default. Although queued events are persisted and retried on the next app launch, callingawait htClient.flush()immediately after high-value events like purchases or sign-ups ensures they are delivered promptly rather than waiting for the next flush cycle or app session. -
Add a
BackgroundFlushPolicy. This ensures the queue is flushed whenever the app moves to the background, covering cases where users leave the app between automatic flush cycles. You can add it at runtime without replacing your existing policies:import { BackgroundFlushPolicy } from "@ht-sdks/events-sdk-react-native"; htClient.addFlushPolicy(new BackgroundFlushPolicy()); -
Enable debug logging. Set
debug: trueincreateClient(this is the default) and check device logs for errors during event delivery. -
Verify your write key. Confirm that the
writeKeyincreateClientmatches the write key shown on your event source in Hightouch.