Skip to content
Docs menu · Expo

Install — Mobile

Install AnyReplay in an Expo App

Add AnyReplay session replay to an Expo app with no native module, no config plugin and no prebuild, including Expo Router screen tracking and consent.

Because @anyreplay/react-native is plain JavaScript, it installs into an Expo project like any other library: no config plugin, no prebuild, and it runs in Expo Go as well as in development and production builds. The only requirement it adds is the New Architecture, which the recorder uses to measure what is on screen: on by default from Expo SDK 52 (React Native 0.76), in Expo Go and in builds. On the old architecture the SDK still installs and runs: it logs one warning and records taps, screen names and events, but not what is on screen, so the replay has no picture. For an overview of what to expect before you install, see session replay for Expo.

Install

npx expo install @anyreplay/react-native @react-native-async-storage/async-storage

AsyncStorage is optional. It lets the recorder recognise a returning visitor and resume a session after a relaunch; without it, ids are kept in memory.

Expo Router

With Expo Router the root layout is the place to start the recorder and to wrap the app. init goes at module level so it runs once.

import AsyncStorage from '@react-native-async-storage/async-storage';
import { AnyReplay, init, screen } from '@anyreplay/react-native';
import { Stack, usePathname } from 'expo-router';
import { useEffect } from 'react';

init({
  projectKey: process.env.EXPO_PUBLIC_ANYREPLAY_KEY!,
  storage: AsyncStorage,
});

export default function RootLayout() {
  const pathname = usePathname();
  useEffect(() => {
    screen(pathname);
  }, [pathname]);

  return (
    <AnyReplay>
      <Stack />
    </AnyReplay>
  );
}
app/_layout.tsx

Expo Router manages the navigation container for you, so instead of trackNavigation this reports each path to screen(). Repeated calls with the same name are harmless — each one marks a screen change, so the effect above runs only when the path changes.

Classic App.tsx

If your Expo app uses React Navigation directly, follow the React Native guide unchanged and use trackNavigation on your NavigationContainer.

Environment variables

Expo inlines variables prefixed with EXPO_PUBLIC_ into the JavaScript bundle. The project key is public, so that is fine; the name is only a suggestion. Remember that a native app sends no Origin header: if the project lists allowed domains or apps, put the app's id under Allowed apps and pass it as appId.

init({ projectKey: process.env.EXPO_PUBLIC_ANYREPLAY_KEY!, requireConsent: true });

// when the person agrees — right after init is fine
import { consent } from '@anyreplay/react-native';
consent(true);

Nothing is recorded, and nothing is written to storage, until consent(true). Calling it before init has finished is fine: the decision is applied the moment it does, and an early consent(false) keeps the recorder from ever starting. Store the choice yourself and call consent(true) on each launch for people who have already agreed; consent(false) removes the stored ids. See consent.

Checking the install

  • Pass debug: true to init to see warnings in the Metro log: an unmounted <AnyReplay> or a refused request. The old-architecture warning appears even without it.
  • Open the app, move between two screens, then send it to the background — that flushes the buffer — and look for the session in Sessions.
  • If requests are refused with 403, add the app's id under Allowed apps and pass the same value as appId.