sdk-react-native Reference

@emit-vision/sdk-react-native wraps sdk-js for React Native environments, replacing browser-specific APIs with React Native equivalents. It works with both bare React Native and Expo.

Installation

npm install @emit-vision/sdk-react-native @emit-vision/sdk-js

Optional (recommended):

npm install @react-native-async-storage/async-storage @react-native-community/netinfo

Quick start

Call initRN early in your app (e.g., App.tsx) and pass in platform APIs you want to use:

import { useEffect } from "react";
import { AppState, ErrorUtils } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import NetInfo from "@react-native-community/netinfo";
import { initRN } from "@emit-vision/sdk-react-native";
 
export function App() {
  useEffect(() => {
    let client: Awaited<ReturnType<typeof initRN>> | undefined;
 
    initRN({
      dsn: process.env.EXPO_PUBLIC_EMIT_VISION_DSN,
      environment: process.env.NODE_ENV,
      asyncStorage: AsyncStorage,
      appState: AppState,
      netInfo: NetInfo,
      errorUtils: ErrorUtils,
    }).then((c) => {
      client = c;
    });
 
    return () => {
      client?.close();
    };
  }, []);
 
  return <Router />;
}

All asyncStorage, appState, netInfo, and errorUtils options are optional — pass only what you need.

initRN(options)

Async function that initialises the SDK and sets up React Native lifecycle hooks.

import { initRN } from "@emit-vision/sdk-react-native";
 
const client = await initRN({
  apiKey: "your-api-key",
  endpoint: "https://your-emit-vision-instance.com",
  asyncStorage: AsyncStorage, // persists anonymous session ID
  appState: AppState,         // flushes events when app goes to background
  netInfo: NetInfo,           // flushes events when connectivity is restored
  errorUtils: ErrorUtils,     // captures unhandled JS errors
});
 
// Later, when shutting down:
client.close();

Options

All sdk-js init() options are accepted, plus:

OptionTypeDescription
asyncStorageAsyncStorageLikePersists the anonymous session ID across app restarts
appStateAppStateLikeFlushes the event queue when the app goes to background
netInfoNetInfoLikeFlushes when the device comes back online
errorUtilsErrorUtilsLikeCaptures unhandled JS errors via ErrorUtils.setGlobalHandler

Auto-capture

  • Page views: disabled — there is no DOM in React Native.
  • Unhandled JS errors: set via ErrorUtils.setGlobalHandler when errorUtils is provided and autoCapture.errors is not false.
  • Unhandled promise rejections: not automatically captured in RN (different runtime guarantees). Track manually with captureError.

useEmitVision()

Returns all SDK capture functions for use inside components.

import { useEmitVision } from "@emit-vision/sdk-react-native";
 
function SaveButton() {
  const { captureEvent } = useEmitVision();
 
  return (
    <TouchableOpacity onPress={() => captureEvent("settings_saved")}>
      <Text>Save</Text>
    </TouchableOpacity>
  );
}

Available methods:

MethodDescription
captureEvent(name, props?)Record a product event
captureError(error, opts?)Record a handled error
identify(userId, traits?)Associate events with a user
group(groupId, traits?)Associate events with a group
consent()Unlock the queue after user consent
flush()Force an immediate send
setContext(ctx)Add ambient context to all subsequent events
setTags(tags)Add ambient tags to all subsequent events

Identifying users

import { identify } from "@emit-vision/sdk-react-native";
 
// After login:
identify("user_123", { email: "[email protected]" });

Same as sdk-js — pass consentRequired: true to initRN and call consent() after the user accepts:

await initRN({ apiKey: "...", consentRequired: true });
 
// After user accepts:
import { consent } from "@emit-vision/sdk-react-native";
await consent();

Injecting platform APIs

If you prefer not to install the optional peer packages, you can adapt any storage or network layer by implementing the corresponding interface:

import type { AsyncStorageLike } from "@emit-vision/sdk-react-native";
import { MMKV } from "react-native-mmkv";
 
const mmkv = new MMKV();
 
const mmkvStorage: AsyncStorageLike = {
  getItem: (key) => Promise.resolve(mmkv.getString(key) ?? null),
  setItem: (key, value) => {
    mmkv.set(key, value);
    return Promise.resolve();
  },
};
 
await initRN({ apiKey: "...", asyncStorage: mmkvStorage });

Batch compression

Event batches larger than 8 KB are automatically gzipped and sent with the Content-Encoding: gzip header. The API accepts both compressed and uncompressed bodies.

React Native runtimes vary in their support for the standard CompressionStream API — notably, some older or polyfilled environments lack it entirely. If CompressionStream is unavailable, the SDK automatically falls back to sending uncompressed JSON — no configuration needed.