ChangelogBook a demoSign up

React Native SDK

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 \
  @ht-sdks/events-sdk-react-native \
  @ht-sdks/sovran-react-native \
  react-native-get-random-values \
  @react-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:

ParameterTypeDescription
writeKeyStringThe write key used to identify the source in Hightouch.
collectDeviceIdBooleanWhether 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.
debugBooleanWhether to generate debug logs. Defaults to true.
loggerLoggerCustom logger instance to redirect internal logging from the library.
flushAtNumberMaximum number of events to accumulate before sending to the Hightouch API. Defaults to 20.
flushIntervalNumberMaximum number of seconds to hold events before sending to the Hightouch API. Defaults to 30.
flushPoliciesArrayOptional 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.
maxBatchSizeNumberThe 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.
trackAppLifecycleEventsBooleanWhether to automatically track lifecycle events like Application Installed. Defaults to false.
trackDeepLinksBooleanWhether to automatically track deep links. Only supported for Android. Defaults to false.
proxyStringUsed 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:

ParameterTypeDescription
userIdStringThe user's persistent ID.
traitsObjectAdditional 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:

ParameterTypeDescription
eventStringThe event name (for example "Checked Out").
propertiesObjectAdditional 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:

ParameterTypeDescription
nameStringThe screen's name. For example, "Getting started".
propertiesObjectAdditional properties about the event.

Group

The group method sends a group event.

Method signature:

htClient.group(groupId, [traits])

Method parameters:

ParameterTypeDescription
groupIdStringThe id for the group.
traitsObjectAdditional 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 the flushAt option in createClient).
  • Time interval (flushInterval): 30 seconds have elapsed since the last flush (configurable via the flushInterval option in createClient).

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 the flushAt default.
  • TimerFlushPolicy(intervalMs) — flushes on a recurring time interval (in milliseconds). This is the policy behind the flushInterval default.
  • 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

  1. Add the Android AAID plugin to your project.

    npm install @ht-sdks/events-sdk-react-native-plugin-advertising-id
    
  2. Add the plugin to your code by adding the following code after initializing your client.

    htClient.add({plugin: new AdvertisingIdPlugin()});
    
  3. 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

  1. Add the iOS IDFA plugin to your project.

    npm install @ht-sdks/events-sdk-react-native-plugin-idfa
    
  2. Add the plugin to your code by adding the following code after initializing your client.

    htClient.add({plugin: new IdfaPlugin()});
    
  3. Update your Info.plist to 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:

  1. Upgrade to the latest SDK version. Run npm install @ht-sdks/events-sdk-react-native@latest to pick up the most recent bug fixes and delivery improvements.

  2. Add a manual flush after 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, calling await 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.

  3. 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());
    
  4. Enable debug logging. Set debug: true in createClient (this is the default) and check device logs for errors during event delivery.

  5. Verify your write key. Confirm that the writeKey in createClient matches the write key shown on your event source in Hightouch.

Ready to get started?

Jump right in or a book a demo. Your first destination is always free.

Book a demoSign upBook a demo

Need help?

Our team is relentlessly focused on your success. Don't hesitate to reach out!

Feature requests?

We'd love to hear your suggestions for integrations and other features.

Privacy PolicyTerms of Service

Last updated: May 19, 2026

On this page
  • Installation
  • iOS
  • Android
  • SDK Initialization
  • Manual tracking API
  • Identify
  • Track
  • Screen
  • Group
  • Reset
  • Flush
  • Anonymous ID management
  • Get the anonymous ID
  • Set or override the anonymous ID
  • Relationship between anonymousId and userId
  • Out of the box events
  • Lifecycle events
  • Advertising identifiers
  • Android AAID
  • iOS IDFA
  • Tips and troubleshooting
  • Missing events
Send feedback