Documentation
Getting started

Quickstart: RN / Expo

Add FounderHQ to a React Native or Expo app.

Install the SDK, create the client once, and see your first event in FounderHQ. About 5 minutes.

Before you start

You need a FounderHQ account and a publishable key. In the app, open Sequences → Settings → Event API keys, create a key for Browser/mobile, and copy it. The full key is shown only once.

Install the SDK

npx expo install @founderhq/events-react-native \
  @react-native-async-storage/async-storage \
  expo-application expo-device expo-localization

The three Expo packages let the SDK report app version, device model, locale, and time zone. Building bare React Native? Install @founderhq/events-react-native and @react-native-async-storage/async-storage with npm, and skip them.

Create the client once

Create one module and import it everywhere.

// lib/analytics.ts
import { createFounderHqExpoClient } from "@founderhq/events-react-native/expo";

export const events = createFounderHqExpoClient("fhq_pk_XXXX");

Bare React Native uses the same API without the Expo context:

import { FounderHqReactNativeClient } from "@founderhq/events-react-native";

export const events = new FounderHqReactNativeClient("fhq_pk_XXXX");

App lifecycle, sessions, and on-device queuing start immediately. The queue survives a restart, so events captured offline still arrive.

Track screens

Expo Router:

import { usePathname } from "expo-router";
import { useEffect, useMemo } from "react";
import { createExpoRouterTracker } from "@founderhq/events-react-native";
import { events } from "./lib/analytics";

export function ScreenTracker() {
  const pathname = usePathname();
  const trackScreen = useMemo(() => createExpoRouterTracker(events), []);
  useEffect(() => {
    trackScreen(pathname);
  }, [pathname, trackScreen]);
  return null;
}

React Navigation instead? Pass your navigationRef to createReactNavigationTracker(events, navigationRef) and spread the returned onReady and onStateChange onto <NavigationContainer>.

Identify and capture

events.identify("user_8421", { email: "jane@acme.com", plan: "pro" });
events.capture("trial.started", { plan: "pro" });

Pass your own stable user ID, not an email. Call events.reset() on sign-out.

Verify

  1. Run the app on a simulator or device and trigger the event.
  2. In FounderHQ, open Sequences → Events.
  3. You see trial.started with plan: pro, a $session_start, and a $screen for the screen you opened.
  4. Open Sequences → Contacts. Jane is there with her email.

Events can take a few seconds to appear. The SDK batches them. To send immediately, await events.flush().

Troubleshoot

Events only appear after a restart

That is the queue doing its job. It flushes on a timer, on batch size, and when the app goes to the background.

Purchases are not attributed

In-app purchases need the purchase claim flow, not capture. See Mobile purchase claims.

Next

AI agent or LLM? Read this page as markdown

On this page