Phase
Get Started

React Native

llms.txt

Installation

Install the SDK

bun add phase-analytics

Install Required Peer Dependencies

Phase Analytics requires the following React Native packages to function properly:

bun add @react-native-async-storage/async-storage react-native-device-info react-native-localize

These dependencies are required for the SDK to work correctly. The SDK uses:

  • @react-native-async-storage/async-storage - Local storage for the persistent offline queue
  • react-native-device-info - Device information collection
  • react-native-localize - Locale and timezone detection

Optional package:

bun add @react-native-community/netinfo
  • @react-native-community/netinfo - Offline detection. Without it, the SDK keeps sending and relies on retries

Automatic screen tracking (trackNavigation) works with your own React Navigation setup. The SDK does not depend on @react-navigation/native, you only pass it a navigationRef.

Install iOS Dependencies

For iOS, install CocoaPods dependencies:

cd ios && pod install

Get Your API Key

  1. Sign in to Phase Dashboard
  2. Create a new project or select an existing one
  3. Open API Keys tab
  4. Copy your API Key (starts with phase_)

Setup

Wrap your app with the PhaseProvider component to initialize the SDK:

React
App.tsx
import { PhaseProvider } from 'phase-analytics/react-native';
import { NavigationContainer } from '@react-navigation/native';

export default function App() {
  return (
    <NavigationContainer>
      <PhaseProvider apiKey="phase_xxx">
        <YourApp />
      </PhaseProvider>
    </NavigationContainer>
  );
}

The PhaseProvider only initializes the SDK. Call Phase.identify() to register the device and start a session. Events tracked before identify() runs are buffered and sent afterwards.

Configuration

The PhaseProvider component accepts the following props:

Prop

Type

Usage

Identify User

Call Phase.identify() once at startup. This registers the device and starts a session. Calls made before PhaseProvider mounts (for example from child effects) are queued and run after initialization.

React
App.tsx
import { Phase } from 'phase-analytics/react-native';
import { useEffect } from 'react';

export default function App() {
  useEffect(() => {
    // Initialize analytics - no PII collected by default
    Phase.identify();
  }, []);

  return <YourApp />;
}

Privacy by default:

  • No personal data is collected without explicit properties
  • Device ID is auto-generated and stored locally
  • Only technical metadata is collected (OS version, platform, locale)

Adding custom properties:

You can optionally attach user properties. Important: If you add PII (personally identifiable information), ensure you have proper user consent:

// After user login and consent
await Phase.identify({
  user_id: '123',
  plan: 'premium',
  beta_tester: true
});

// ⚠️ If adding PII, get consent first
const hasConsent = await customGetUserConsent();
if (hasConsent) {
  await Phase.identify({
    email: '[email protected]',
    name: 'John Doe'
  });
}

Properties must be primitives: string, number, boolean, or null.

Track Events

Track custom events with optional parameters. Events tracked before Phase.identify() runs are buffered in memory (up to 200 events, for up to one hour) and sent once the session exists. If more than 200 pile up, the oldest are dropped.

// Event without parameters
Phase.track('app_opened');

// Event with parameters
Phase.track('purchase_completed', {
  amount: 99.99,
  currency: 'USD',
  product_id: 'premium_plan'
});

Event naming rules:

  • Alphanumeric characters, underscores (_), hyphens (-), periods (.), forward slashes (/), and spaces
  • 1-256 characters
  • Examples: purchase, user.signup, payment/success, Button Clicked

Event parameters:

  • Flat primitive object only
  • Values must be string, number, boolean, or null
  • Max 32 keys
  • Key max 32 characters
  • String value max 256 characters
  • Serialized payload max 8 KB
  • Empty objects are normalized away

Automatic Screen Tracking

Screen tracking is off by default. Enable it by setting trackNavigation to true and passing a navigationRef:

React
App.tsx
import { PhaseProvider } from 'phase-analytics/react-native';
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

export default function App() {
  const navigationRef = useNavigationContainerRef();

  return (
    <NavigationContainer ref={navigationRef}>
      <PhaseProvider
        apiKey="phase_xxx"
        trackNavigation={true}
        navigationRef={navigationRef}
      >
        <Stack.Navigator>
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Settings" component={SettingsScreen} />
        </Stack.Navigator>
      </PhaseProvider>
    </NavigationContainer>
  );
}

How it works:

  • Requires navigationRef from useNavigationContainerRef() hook
  • Pass the ref to both NavigationContainer and PhaseProvider
  • Automatically tracks screen views on navigation state changes
  • Screen names are converted from route names
  • Works with nested navigators and dynamic routes

Important: The navigationRef must be passed to both NavigationContainer and PhaseProvider for automatic tracking to work. Without it, trackNavigation logs an error and no screens are tracked.

Manual Screen Tracking

Use Phase.trackScreen() when trackNavigation is off or you use a custom navigator. params follow the same rules as Phase.track().

Phase.trackScreen('/settings/profile');

Phase.trackScreen('/settings/profile', { tab: 'billing' });

Flush

Events are sent automatically in batches (see Delivery), so you rarely need this. Phase.flush() sends queued data right away and resolves when the attempt finishes.

await Phase.flush();

Reset

Use Phase.reset() to forget the current device, for example on logout. It ends the session and generates a new device ID. Data already queued for the previous device is still delivered. Call Phase.identify() again to start tracking.

await Phase.reset();
await Phase.identify();

Type Reference

DeviceProperties

Custom user/device attributes passed to Phase.identify():

Prop

Type

EventParams

Event parameters passed to Phase.track() and Phase.trackScreen():

Prop

Type

How It Works

Delivery

Devices, sessions, events and heartbeats go through a persistent queue and are sent to the batch endpoint:

  • Every ~10 seconds while data is queued, as soon as 20 items are queued, and when the app goes to the background
  • Items leave the queue only after the server accepts them. Failed requests are retried with backoff and honor Retry-After
  • Changing the API key clears data queued under the previous key

Offline Support

The queue survives app restarts. It is stored with @react-native-async-storage/async-storage and capped at ~1.5 MB, dropping the oldest sessions first when full.

With @react-native-community/netinfo installed, nothing is sent while the device is offline and only the latest heartbeat per session is kept. Queued data is sent when the connection is restored.

Privacy

  • No personal data is collected by default
  • Device IDs are generated locally and stored persistently
  • Geolocation is resolved server-side from IP address (disable with properties)
  • All data collection is optional via configuration

Performance

  • Queued data is batched and sent asynchronously
  • Failed requests retry with exponential backoff
  • Maximum batch size: 50 items per request
  • Offline queue capped at 1000 items / ~1.5 MB