Installation
Add the Rejourney package to your project using npm or yarn.
npm install @rejourneyco/react-native[!NOTE] Rejourney requires native code and is not compatible with Expo Go. Use development builds:
npx expo run:ios npx expo run:android
3 Line Setup
Initialize and start Rejourney at the top of your app (e.g. in App.tsx or index.js).
import { Rejourney } from '@rejourneyco/react-native';Rejourney.init('pk_live_your_public_key');Rejourney.start();Requires no provider wrapping. Recording starts immediately.
Remote Recording Settings
Project Settings can control React Native recording defaults without shipping a new app build. Supported SDK versions read the remote recording FPS setting on session start; the default is 1 FPS, and project admins can choose 1, 2, or 3 FPS. If the remote config is unavailable, the SDK falls back to local/default capture behavior.
Screen Tracking
Rejourney automatically tracks screen changes so you can see where users are in your app during replays. Choose the setup that matches your navigation library:
Expo Router (Automatic)
If you use Expo Router, screen tracking works out of the box. No additional code is needed.
[!TIP] Using custom screen names? If you use Expo Router but want to provide your own screen names manually, see the Custom Screen Names section below.
React Navigation
If you use React Navigation (@react-navigation/native), use the useNavigationTracking hook in your root NavigationContainer:
import { Rejourney } from '@rejourneyco/react-native';import { NavigationContainer } from '@react-navigation/native';function App() { const navigationTracking = Rejourney.useNavigationTracking(); return ( <NavigationContainer {...navigationTracking}> {/* Your screens */} </NavigationContainer> );}Custom Screen Names
If you want to manually specify screen names (e.g., for analytics consistency or if you don't use the libraries above), use the trackScreen method.
For Expo Router users:
To use custom names with Expo Router, you must first disable automatic tracking in your configuration:
Rejourney.init('pk_live_your_public_key', { autoTrackExpoRouter: false});Manual tracking call:
Call trackScreen whenever a screen change occurs:
import { Rejourney } from '@rejourneyco/react-native';// Call this in your screen component or navigation listenerRejourney.trackScreen('Checkout Page');User Identification
Associate sessions with your internal user IDs to filter and search for specific users in the dashboard.
import { Rejourney } from '@rejourneyco/react-native';// After loginRejourney.setUserIdentity('user_abc123');// On logoutRejourney.clearUserIdentity();[!IMPORTANT] Privacy: Use internal IDs or UUIDs. If you must use PII (email, phone), hash it before sending.
Custom Events
Track meaningful user actions to understand behavior patterns, debug issues, and filter session replays in the dashboard.
Basic Usage
import { Rejourney } from '@rejourneyco/react-native';// Simple event (name only)Rejourney.logEvent('signup_completed');// Event with propertiesRejourney.logEvent('button_clicked', { buttonName: 'signup' });API
Rejourney.logEvent(name: string, properties?: Record<string, unknown>)| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Event name — use snake_case for consistency |
properties | object | No | Key-value pairs attached to this specific event occurrence |
Examples
// E-commerceRejourney.logEvent('purchase_completed', { plan: 'pro', amount: 29.99, currency: 'USD'});// OnboardingRejourney.logEvent('onboarding_step', { step: 3, stepName: 'profile_setup', skipped: false});// Feature usageRejourney.logEvent('feature_used', { feature: 'dark_mode', enabled: true});// Errors / edge casesRejourney.logEvent('payment_failed', { errorCode: 'card_declined', retryCount: 2});How Events Appear in the Dashboard
Custom events are stored per-session and visible in two places:
- Session Replay Timeline — Events appear as markers on the replay timeline so you can jump to the exact moment an action occurred.
- Session Archive Filters — Filter the session list by:
- Event name — Find all sessions containing a specific event (e.g.
purchase_completed) - Event property — Narrow further by property key and/or value (e.g.
plan = pro) - Event count — Find sessions with a specific number of custom events (e.g. more than 5 events)
- Event name — Find all sessions containing a specific event (e.g.
Best Practices
[!TIP]
- Use consistent naming (
snake_case, e.g.button_clickednotButton Clicked)- Keep property values simple (strings, numbers, booleans) — avoid nested objects
- Focus on actions that matter for debugging or analytics — don't log everything
- Properties are for per-event context. For session-level attributes, use Metadata instead
Metadata
Attach session-level key-value pairs that describe the user or session context. Unlike events, metadata is set once per key and applies to the entire session.
import { Rejourney } from '@rejourneyco/react-native';// Set a single propertyRejourney.setMetadata('plan', 'premium');// Set multiple properties at onceRejourney.setMetadata({ role: 'admin', segment: 'enterprise', ab_variant: 'checkout_v2'});When to Use Metadata vs Events
| Use Case | Use Metadata | Use Events |
|---|---|---|
| User's subscription plan | setMetadata('plan', 'pro') | |
| User clicked a button | logEvent('button_clicked', { buttonName: 'signup' }) | |
| A/B test variant | setMetadata('ab_variant', 'v2') | |
| Purchase completed | logEvent('purchase', { amount: 29 }) | |
| User's role | setMetadata('role', 'admin') | |
| Onboarding step reached | logEvent('onboarding_step', { step: 3 }) |
Rule of thumb: If it describes who the user is or what state they're in, use metadata. If it describes something that happened, use events.
Privacy Controls
Text inputs and camera views are automatically masked by default. Project admins can change the default text input masking level in Project Settings for supported SDK versions; older SDK versions ignore that remote setting and keep their existing masking behavior. Secure/password fields, camera views, and explicit masks remain protected.
To manually hide additional sensitive UI, wrap components in the Mask component:
import { Mask } from '@rejourneyco/react-native';<Mask> <Text>Account balance: $5,000</Text></Mask>Masked content appears as a solid rectangle in replays and is never captured at the source.
User Consent & GDPR
[!IMPORTANT] You are the Data Controller. Rejourney acts as a Data Processor on your behalf. You are responsible for ensuring your end-users are informed about session recording and that you have a valid legal basis for processing their data (e.g. consent or legitimate interests).
What you must do
-
Disclose session recording in your app's privacy policy. Include language such as:
"We use Rejourney to record anonymized AND non-anonymized session replays of your in-app activity to help us improve the product, track crashes and issues, and reduce product friction. Session data may include screen interactions, device information, and approximate location. Text inputs and sensitive UI elements are automatically masked and never captured."
-
Gate recording behind consent (recommended for EEA users):
// Only start recording after the user accepts your privacy policy / consent promptRejourney.init('pk_live_your_public_key');// Call this after consent is confirmedfunction onUserConsented() {Rejourney.start();} -
Respect opt-outs. If a user withdraws consent, stop recording and clear their data:
Rejourney.stop();Rejourney.clearUserIdentity();
Console log capture
Console log capture is enabled by default (trackConsoleLogs: true). Console logs can contain PII depending on your app's logging practices. Disable it if sensitive data may appear in logs:
Rejourney.init('pk_live_your_public_key', { trackConsoleLogs: false });Geolocation
IP-derived geolocation (country, region, city) is collected by default. When collectGeoLocation is false, the SDK passes a flag to the native layer that suppresses the IP geolocation lookup on the backend — no location data is stored for that session. Disable it if you do not need location data or want to minimise data collection for EEA users:
Rejourney.init('pk_live_your_public_key', { collectGeoLocation: false });Native sheets
Native sheet capture is enabled by default (captureNativeSheets: true) for supported SDK versions. This allows app-owned native sheets and dialogs, such as payment authorization modals, to appear in debugging replays when the OS permits capture. Keyboard/text-input system sheets are excluded when text inputs are masked by default. When text input masking is set to secure fields only, keyboards are best-effort only and cannot be reliably captured, especially when the OS renders them as protected or remote surfaces. OS share sheets are also best-effort only and cannot be reliably captured when the system renders them as protected or remote surfaces.
Disable native sheet capture if you want visual replay to stay limited to the main app window:
Rejourney.init('pk_live_your_public_key', { captureNativeSheets: false });Observe-Only Mode (No Visual Recording)
To capture errors, crashes, ANRs, and network activity without recording visual replays, set observeOnly: true:
Rejourney.init('pk_live_your_public_key', { observeOnly: true });When enabled, all telemetry is collected but no screenshots are taken — sessions WILL NOT appear in your Replays Page but there will be full analytics/error/network/crash data. No replay. This is useful when users have opted out of screen recording but you still want error visibility.
Note: This can be set conditionally per user, for example based on a stored preference or consent flag:
const userOptedOutOfRecording = await getUserPreference('noRecording');Rejourney.init('pk_live_your_public_key', { observeOnly: userOptedOutOfRecording });