Sign up

Installation guides

React Native & Expo

On this page

@metricraven/react-native records screen views, goals and identified users from your app. Events wait in a queue while the device is offline and are sent when it reconnects.

Set up

  1. Allow your app in Settings

    In your site's Settings, add your bundle ID (iOS) or package name (Android) under Mobile apps, for example com.example.app. App events from IDs that aren't listed are rejected.

  2. Install the SDK

    AsyncStorage is optional, but without it queued events are lost if the app is closed while offline.

    terminal
    npx expo install @metricraven/react-native @react-native-async-storage/async-storage
  3. Create the client once

    analytics.ts
    // analytics.ts
    import AsyncStorage from "@react-native-async-storage/async-storage";
    import * as Application from "expo-application";
    import { createMetricRaven } from "@metricraven/react-native";
    
    export const analytics = createMetricRaven({
      siteId: process.env.EXPO_PUBLIC_METRICRAVEN_SITE_ID!,
      appId: Application.applicationId ?? "com.example.app", // listed under Settings → Mobile apps
      appVersion: Application.nativeApplicationVersion ?? undefined,
      appBuild: Application.nativeBuildVersion ?? undefined,
      storage: AsyncStorage, // keeps the offline queue (and, with consent, the visitor ID)
      disabled: __DEV__,
    });
  4. Track screens

    With Expo Router, track the route pattern from your root layout:

    app/_layout.tsx
    // app/_layout.tsx
    import { Slot, useSegments } from "expo-router";
    import { MetricRavenProvider, useExpoRouterTracking } from "@metricraven/react-native";
    import { analytics } from "../analytics";
    
    function ScreenTracking() {
      useExpoRouterTracking(useSegments()); // "/users/[id]", never the real ID
      return null;
    }
    
    export default function RootLayout() {
      return (
        <MetricRavenProvider client={analytics}>
          <ScreenTracking />
          <Slot />
        </MetricRavenProvider>
      );
    }

    With React Navigation, pass the helpers to your container. It records route names, not params:

    App.tsx
    // App.tsx
    import { NavigationContainer, useNavigationContainerRef } from "@react-navigation/native";
    import { MetricRavenProvider, trackNavigation } from "@metricraven/react-native";
    import { analytics } from "./analytics";
    
    export default function App() {
      const navigationRef = useNavigationContainerRef();
      return (
        <MetricRavenProvider client={analytics}>
          <NavigationContainer ref={navigationRef} {...trackNavigation(analytics, navigationRef)}>
            {/* your navigators */}
          </NavigationContainer>
        </MetricRavenProvider>
      );
    }
  5. Send events and identify users

    server.ts
    analytics.track("cta_click", { source: "home" });
    analytics.screen("Paywall"); // a screen view outside your navigator
    analytics.identify(user.id, { name: user.name, email: user.email, plan: "pro" });
    analytics.reset(); // on logout
    analytics.setConsent(true); // from your consent prompt
    analytics.openedFromUrl(url); // utm_* from a deep link, for the next screen view
    
    // In components: const analytics = useMetricRaven(); useScreen("Settings");

Verify it works

Open the app on a device or simulator with disabled off. The screen appears in Realtime. Screens are listed under Pages as paths like /Home or /users/[id], and your app ID appears under Hostnames.

Client

CallNotes
screen(name, props?)A screen view. Names like "Settings" become the path /Settings.
track(name, props?)A custom goal, attributed to the current screen. Up to 10 properties.
identify(userId, traits?)Links this device's visitor to your user ID, with optional name, email and up to 10 custom traits.
reset()On logout. Forgets identify state and, with consent, starts a new visitor ID.
setConsent(granted)Grants or withdraws storage consent. Withdrawing also removes the ID from queued events.
openedFromUrl(url)Reads utm_* parameters from a deep link and attaches them to the next screen view.
flush()Sends the queue now. Runs automatically when the app returns to the foreground or goes to the background.
getVisitorId() / onVisitorId()The current visitor ID, and a listener for changes.
OptionWhat it does
siteId, appIdRequired. appId must be listed under Mobile apps.
appVersion, appBuildShown on events and visitor profiles. Read them from expo-application or react-native-device-info.
storageAsyncStorage. Persists the queue and, with consent, the visitor ID.
consentSet true when the user has already allowed analytics storage.
apiHost / endpointWhere events go. Defaults to https://metricraven.com/api/v1/collect.
maxQueueSize, maxEventAgeMsDefaults to 500 events and 7 days. Older or extra events are dropped.
disabledTurns tracking off, for example in development.

Consent and visitor IDs

The app follows the same rules as the website tracker. Until you call setConsent(true) (or pass consent: true), nothing that identifies the device is stored. The server derives a daily visitor ID from the network address and the app's user agent. Raw IP addresses are not kept. With consent, a random ID is kept in storage, so sessions on different days connect. Granting consent also relabels today's events from that device.

Mobile networks change address often, so without consent one person can appear as several visitors in a day. Ask for consent if returning-user numbers matter to you.

Offline queue and retries

Events are sent one at a time, in order. Network errors, timeouts, rate limits and server errors are retried with growing waits, up to 5 minutes, and Retry-After is respected. Rejected events are dropped instead of retried. That covers invalid data, an app ID that isn't listed, an unknown site, or paused tracking. Events replayed more than 5 minutes after they happened are recorded at the time they arrive.

Details

  • App traffic is identified by your app ID because native requests have no web page or browser origin. A browser can't use this to send data as your app: browser requests always carry an origin, and those are checked against your domains.
  • Like the website tag, the site ID and app ID aren't secrets. The allowlist keeps out misconfigured and casual traffic, not a determined forger. For verified events, use server events or server identify.
  • Expo Router: use useSegments() rather than usePathname(), so IDs in URLs (like /users/42) don't show up in reports.
  • Purchases inside the app: see RevenueCat and send the same user ID you pass to identify.
  • @metricraven/react-native/core exports the client without React or react-native imports, for tests and other JavaScript runtimes.