AI-powered crash analysis is now available on all plans — including Free.Read the crash analysis guide
Docs/React Native SDK
TypeScript · React Native 0.73+

React Native SDK

Add session replay, crash reporting, and network monitoring to your React Native app in under 5 minutes. Works with Expo managed workflow, Expo bare, and plain React Native. No native module linking required for Expo.

Installation

Install the BugsPulse React Native package from npm. No additional native setup is required for Expo managed projects.

Terminal
npm install @bugspulse/react-native
# or
yarn add @bugspulse/react-native
# or
pnpm add @bugspulse/react-native

Expo managed workflow

No additional steps needed. The package uses Expo-compatible APIs and works out of the box.

Bare React Native

For bare React Native projects (not Expo), run pod install after npm install:

Terminal
cd ios && pod install

Initialization

Call BugsPulse.init() as early as possible — before registerRootComponent or the root render call. The earlier you initialize, the more complete your session data will be.

index.ts
import { AppRegistry } from 'react-native';
import BugsPulse from '@bugspulse/react-native';
import App from './App';

BugsPulse.init({
  apiKey: 'bp_live_your_key_here',   // Required — from project settings
  environment: 'production',          // 'development' | 'staging' | 'production'
});

AppRegistry.registerComponent('MyApp', () => App);
Tip: Your API key is available in the Project Settings page of your BugsPulse dashboard. Never hard-code keys in source control — use environment variables or a secrets manager and inject via EAS Secrets or your CI/CD pipeline.

Expo with app.config.js

index.ts
import Constants from 'expo-constants';
import BugsPulse from '@bugspulse/react-native';

BugsPulse.init({
  apiKey: Constants.expoConfig?.extra?.bugspulseApiKey ?? '',
  environment: __DEV__ ? 'development' : 'production',
});
app.config.js
module.exports = {
  extra: {
    bugspulseApiKey: process.env.BUGSPULSE_API_KEY,
  },
};

Configuration options

All options passed to BugsPulse.init():

OptionTypeDefaultDescription
apiKeystringrequiredYour project API key from the dashboard.
apiUrlstringhttps://api.bugspulse.comOverride the ingest endpoint. Only needed for local development.
environmentstring'production'Tag sessions by environment. Shown as a filter in the dashboard.
captureNetworkRequestsbooleantrueIntercept fetch, XMLHttpRequest, and Axios to log API calls.
captureCrashesbooleantrueHandle unhandled JS errors and Promise rejections.
captureTouchesbooleantrueRecord tap, swipe, and scroll events for replay.
captureNavigationbooleantrueRecord React Navigation screen transitions.
sessionSamplingRatenumber1.0Fraction 0–1 of sessions to capture. 0.5 = 50% of sessions.
flushIntervalMsnumber5000How often (ms) to flush buffered events to the API.
maxOfflineQueueSizenumber1000Max events to buffer when the device is offline.
redactedFieldsstring[][]Additional field names to redact from network request bodies.
debugbooleanfalseLog SDK internals to the console. Useful during integration.

User identification

Associate sessions with an internal user identifier so you can search for all sessions from a specific user. Never pass email addresses, names, or other PII — use an opaque internal user ID.

App.tsx
import BugsPulse from '@bugspulse/react-native';

// Call after the user signs in
BugsPulse.setUser('user_abc123');

// Add structured context to the session
BugsPulse.setContext({
  plan: 'pro',
  appVersion: '2.4.1',
  featureFlags: { newCheckout: true },
});

// Clear identity when the user signs out
BugsPulse.clearUser();
Warning: Never pass email addresses, phone numbers, names, or other personally identifiable information to setUser(). Use an opaque internal ID that you can map to a user in your own systems.

Custom events

Track business-logic events that matter to you. Custom events appear on the session timeline alongside automatic events, making it easy to correlate user actions with crashes or errors.

checkout.ts
import BugsPulse from '@bugspulse/react-native';

// Simple event
BugsPulse.track('checkout_started');

// Event with properties — all properties appear in the replay timeline
BugsPulse.track('checkout_completed', {
  plan: 'pro',
  amount_usd: 29,
  coupon_applied: true,
});

// Add 'screen' to any event to show which screen it happened on
BugsPulse.track('button_press', { screen: 'HomeScreen', buttonId: 'cta' });
BugsPulse.track('product_tap', { screen: 'CatalogScreen', productId: 'sku_123' });

Navigation events

Track screen transitions so the session replay can update the Current Screen indicator as the user moves through your app. Use navigate with from / to, or screen_view with screenName:

navigation.ts
// Track a screen transition
BugsPulse.track('navigate', { from: 'HomeScreen', to: 'CheckoutScreen' });

// Or use screen_view style
BugsPulse.track('screen_view', { screenName: 'ProductDetailScreen' });
Tip: The session replay phone frame shows a live overlay for each event: navigate and screen_view update the current screen label; button_press, product_tap, and other action events show a pulsing badge with the event name and properties; network events show the HTTP method, URL path, and status code. Add meaningful properties to get richer replay context.
Note: Event names should be snake_case verbs describing what the user did. Prefer checkout_completed over CheckoutScreen.

Crash capture

The SDK automatically captures unhandled JS errors and unhandled Promise rejections. You can also capture exceptions manually from try/catch blocks.

payment.ts
import BugsPulse from '@bugspulse/react-native';

// Manual exception capture
async function processPayment(orderId: string) {
  try {
    await paymentService.charge(orderId);
  } catch (error) {
    // Capture with context — the context appears in the crash report
    BugsPulse.captureException(error as Error, {
      orderId,
      context: 'payment_processing',
      retryCount: 2,
    });

    // Re-throw if you want the app to handle it too
    throw error;
  }
}

React error boundary

Wrap your root component with an error boundary to catch render errors and report them before showing a fallback UI:

ErrorBoundary.tsx
import React from 'react';
import BugsPulse from '@bugspulse/react-native';

class ErrorBoundary extends React.Component<
  { children: React.ReactNode; fallback: React.ReactNode },
  { hasError: boolean }
> {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    BugsPulse.captureException(error, {
      context: 'react_error_boundary',
      componentStack: info.componentStack ?? '',
    });
  }

  render() {
    return this.state.hasError ? this.props.fallback : this.props.children;
  }
}

export default ErrorBoundary;

Network monitoring

When captureNetworkRequests: true (the default), the SDK automatically intercepts fetch and XMLHttpRequest calls. Each request is logged with URL, method, status, duration, and request/response size.

Note: Request and response bodies are never captured by default. Only metadata (URL, headers, status, timing) is recorded. Enable body capture selectively via thecaptureRequestBody option — bodies are automatically scrubbed of sensitive fields.

Ignore specific domains

index.ts
BugsPulse.init({
  apiKey: 'bp_live_...',
  networkIgnorePatterns: [
    /analytics.example.com/,   // Regex pattern
    'cdn.example.com',           // Exact domain string
  ],
});

Axios interceptor

If you use Axios, attach the BugsPulse interceptor for richer metadata:

api.ts
import axios from 'axios';
import { createAxiosInterceptor } from '@bugspulse/react-native';

const api = axios.create({ baseURL: 'https://api.example.com' });
createAxiosInterceptor(api);

export default api;

Session control

Sessions are started automatically on init. Call BugsPulse.endSession() when the app moves to the background to record the session duration and mark it as completed.

App.tsx
import { AppState, type AppStateStatus } from 'react-native';
import { useEffect, useRef } from 'react';
import BugsPulse from '@bugspulse/react-native';

export default function App() {
  const appState = useRef(AppState.currentState);

  useEffect(() => {
    const sub = AppState.addEventListener('change', (next: AppStateStatus) => {
      if (appState.current === 'active' && next.match(/background|inactive/)) {
        BugsPulse.endSession();
      }
      if (appState.current.match(/background|inactive/) && next === 'active') {
        BugsPulse.startSession(); // Start a new session on foreground
      }
      appState.current = next;
    });
    return () => sub.remove();
  }, []);

  return <RootNavigator />;
}
Note: Without calling endSession(), all sessions will remain in the active state and the Avg Session Duration metric in analytics will show 0.

Privacy & redaction

BugsPulse is privacy-first by design. The following are never captured:

  • Keyboard input — individual keystrokes are never recorded
  • Clipboard contents
  • Passwords, tokens, and authorization headers (automatically redacted)
  • Request/response bodies (unless explicitly enabled)
  • Camera, microphone, or biometric data
  • Video or screenshots of the screen

Custom redaction rules

Add field names that should be scrubbed from network payloads:

index.ts
BugsPulse.init({
  apiKey: 'bp_live_...',
  redactedFields: [
    'ssn',
    'creditCard',
    'cvv',
    'dob',
    'socialSecurityNumber',
    /secret.*/i,   // Regex patterns also supported
  ],
});

Offline support

Events captured while the device has no connectivity are stored in an in-memory queue and flushed automatically when the connection is restored. The queue defaults to maxOfflineQueueSize: 1000 events. When the limit is reached, the oldest events are dropped to make room for new ones. Queued events are not persisted to disk, so they are lost if the app is killed while offline.

Troubleshooting

Sessions are not appearing in the dashboard

Enable debug: true in your init config and check the console for flush errors. Most common cause is an incorrect API key or a network request being blocked by a proxy.

Network requests are not being captured

Make sure captureNetworkRequests is not set to false. If you're using a custom fetch polyfill, pass it to BugsPulse.init({ fetchOverride: myFetch }).

The SDK is causing bundle size to increase significantly

The SDK is under 5KB gzipped. Check for accidental double-imports or peer dependencies being bundled. Run npx react-native bundle --stats and inspect the output.

Crashes are not being captured in Hermes

Hermes crashes require source maps to be uploaded for symbolication. Use the bugspulse upload-sourcemaps CLI command as part of your release build.