Skip to content

ErrSight/errsight-rn

errsight-rn

React Native client for ErrSight error tracking. Captures errors and log events from your app and ships them to the ErrSight API in batches

Features

  • Automatic JS error capture via React Native's ErrorUtils
  • Unhandled promise rejection tracking (Hermes + JSC)
  • React ErrorBoundary component with reset hook
  • Auto network breadcrumbs — every fetch / XMLHttpRequest call attached as a breadcrumb (URL, method, status, duration)
  • Auto AppState breadcrumbs — foreground/background transitions
  • Optional console breadcrumbsconsole.warn / console.error
  • withScope — push a temporary scope for one event without polluting subsequent events
  • Optional offline persistence (queue survives backgrounding)
  • beforeSend callback for PII scrubbing
  • Retry-with-exponential-backoff on flaky networks
  • Batched event delivery with configurable flush interval
  • Automatic flush on app background/inactive
  • Log levels: debug, info, warning, error, fatal
  • User context attached at the event top level (powers the dashboard's User panel)
  • Device context (platform, OS, OS version) included automatically

Scope

This SDK captures JavaScript errors only. Native crashes (iOS NSException / signals, Android Thread.UncaughtExceptionHandler, out-of-memory kills, ANR, watchdog terminations) are not captured — those require a native module that bridges to platform crash collectors.

If you need native crash coverage, run a dedicated tool like Sentry React Native or Firebase Crashlytics alongside errsight-rn. ErrSight handles your JS-side errors, breadcrumbs, and React component stacks; the other tool catches crashes that originate below the JS bridge.

Installation

npm install errsight-rn
# or
yarn add errsight-rn

Quick Start

Initialise the client once at app startup (e.g. in App.tsx):

import { init } from 'errsight-rn';

init({
  apiKey: 'elp_your_api_key_here',
  environment: 'production',
  minLevel: 'error',
});

That's it — unhandled errors and promise rejections are now captured automatically.

Configuration

Option Type Default Description
apiKey string required Your project API key (elp_...)
host string https://errsight.com ErrSight API host
environment string production Environment name
minLevel string error Minimum level to capture (debug, info, warning, error, fatal)
batchSize number 10 Events per HTTP request
flushInterval number 2000 Background flush interval in ms
captureGlobalErrors boolean true Auto-capture crashes and unhandled rejections
release string Release version tag attached to every event
beforeSend (event) => event | null Filter / mutate events before send. Return null to drop.
storage AsyncStorageLike When set, queue is persisted across app launches. See Offline Persistence.
maxPersistedEvents number 500 Cap on events stored when storage is set.
networkBreadcrumbs boolean true Attach a breadcrumb on every fetch / XMLHttpRequest call. Strips query strings (PII default).
appStateBreadcrumbs boolean true Attach a breadcrumb on every AppState transition.
consoleBreadcrumbs boolean false Wrap console.warn / console.error to attach breadcrumbs. Off by default — dev consoles are noisy.

Manual Logging

import { getClient } from 'errsight-rn';

const client = getClient();

client?.debug('Cache miss', { metadata: { key: 'user:42' } });
client?.info('User signed in');
client?.warn('Deprecated API called');
client?.error('Payment failed', { metadata: { orderId: '123' } });
client?.fatal('Database unreachable');

Capturing Errors

try {
  await riskyOperation();
} catch (err) {
  client?.captureError(err, {
    metadata: { operation: 'riskyOperation' },
  });
}

User Context

Attach user info so errors are linked to the affected user:

// After sign-in
client?.setUser({ id: 42, email: 'user@example.com' });

// After sign-out
client?.clearUser();

React Integration

ErrorBoundary

Wrap your component tree to catch render errors:

import { ErrorBoundary } from 'errsight-rn/react';

function App() {
  return (
    <ErrorBoundary fallback={<Text>Something went wrong.</Text>}>
      <MainScreen />
    </ErrorBoundary>
  );
}

With a render-prop fallback for reset:

<ErrorBoundary
  fallback={({ error, reset }) => (
    <View>
      <Text>{error.message}</Text>
      <Button title="Try again" onPress={reset} />
    </View>
  )}
  onReset={() => {
    // Clear whatever caused the error before children re-mount —
    // e.g. invalidate stale queries, navigate to a known-good route.
    queryClient.clear();
  }}
>
  <Dashboard />
</ErrorBoundary>

ErrsightProvider & useErrsight

Access the client from any component via context:

import { ErrsightProvider, useErrsight } from 'errsight-rn/react';

function App() {
  return (
    <ErrsightProvider>
      <MainScreen />
    </ErrsightProvider>
  );
}

function MainScreen() {
  const logger = useErrsight();

  return (
    <Button
      title="Log event"
      onPress={() => logger?.info('Button pressed')}
    />
  );
}

Temporary Scope (withScope)

When you want a tag, user override, or extra breadcrumb for one specific event — without affecting subsequent events on this client — use withScope. State is pushed at the start of the block and restored when it returns (even on throw).

import { getClient } from 'errsight-rn';

const client = getClient();

client?.withScope((scope) => {
  scope.setTag('feature', 'checkout');
  scope.setUser({ id: 'guest-42' });
  scope.captureError(err);
});

// Tag and user are reverted here.

This is the right tool when you want to add error-specific context without bleeding it into the next 50 unrelated events.

PII Scrubbing

For compliance-conscious apps, use beforeSend to scrub fields before events leave the device. The callback receives the event hash and returns the (possibly modified) hash — or null to drop the event entirely.

init({
  apiKey: 'elp_...',
  beforeSend: (event) => {
    // Drop noisy errors entirely
    if (event.message.includes('Network request failed')) return null;

    // Scrub a sensitive metadata field
    if (event.metadata && 'auth_token' in event.metadata) {
      event.metadata.auth_token = '[REDACTED]';
    }
    return event;
  },
});

If beforeSend throws, the original event is sent through unmodified — a buggy filter shouldn't silently lose production errors.

Offline Persistence

By default, the in-memory queue is lost when the app is killed (background-kill, force-quit). For mobile apps where errors often happen moments before a crash, you typically want the queue to survive a cold restart.

Pass any AsyncStorage-shaped backend as storage:

import AsyncStorage from '@react-native-async-storage/async-storage';
import { init } from 'errsight-rn';

init({
  apiKey: 'elp_...',
  storage: AsyncStorage,
});

On next launch, persisted events are prepended to the new in-memory queue and ship in chronological order. The persisted queue is capped at maxPersistedEvents (default 500) — older events are dropped when the cap is exceeded.

@react-native-async-storage/async-storage is the typical choice, but anything matching { getItem, setItem, removeItem } (returning Promises) works — including MMKV with a thin adapter.

Network Breadcrumbs

fetch and XMLHttpRequest are auto-instrumented at init. Every non-self HTTP call becomes a breadcrumb attached to the next event:

GET 200 /users          (47ms)
POST 500 /charges       (892ms)
GET FAILED /heartbeat   network error

Query strings are stripped from breadcrumb URLs (tokens commonly live there). Calls to the SDK's own ingestion endpoint are skipped to avoid recursion. Disable with networkBreadcrumbs: false if you do your own instrumentation or have privacy concerns about request URLs.

Retry Behavior

The SDK keeps events queued (and persisted, if storage is set) until they ship successfully. On transient failures it backs off so it doesn't hammer a dead network:

Response Action
2xx success Reset failure counter, drop sent events.
4xx (client error) Drop the events — retrying won't help.
5xx (server error) Re-queue, exponential backoff (base 1s, cap 60s, jitter 50–100%).
429 Too Many Requests Re-queue, honor Retry-After header (capped at 600s defensively).
Network error (offline, DNS, TLS) Re-queue, exponential backoff, persist to storage.

flush() is a no-op while a backoff is in effect, so periodic flushes don't fire requests during the pause window. Nothing is lost on flaky links — events stay queued across the cooldown and ship when the next flush after the pause expiry succeeds.

Console Breadcrumbs (Opt-In)

Off by default. Enable with consoleBreadcrumbs: true to wrap console.warn and console.error. Useful for targeted debug sessions where you suspect a console message is the missing context, but typically too noisy for production:

init({
  apiKey: 'elp_...',
  consoleBreadcrumbs: __DEV__,
});

The original console methods still fire (so you keep dev-tools output); the wrap just adds a breadcrumb side-effect.

Flushing & Shutdown

Events are flushed automatically every flushInterval ms and when the app moves to background. You can also flush manually:

// Flush immediately (e.g. before navigation)
client?.flush();

// Clean shutdown (flushes remaining events, removes listeners)
client?.shutdown();

License

MIT

About

React Native package

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors