React Native client for ErrSight error tracking. Captures errors and log events from your app and ships them to the ErrSight API in batches
- Automatic JS error capture via React Native's
ErrorUtils - Unhandled promise rejection tracking (Hermes + JSC)
- React
ErrorBoundarycomponent 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 breadcrumbs —
console.warn/console.error withScope— push a temporary scope for one event without polluting subsequent events- Optional offline persistence (queue survives backgrounding)
beforeSendcallback 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
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.
npm install errsight-rn
# or
yarn add errsight-rnInitialise 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.
| 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. |
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');try {
await riskyOperation();
} catch (err) {
client?.captureError(err, {
metadata: { operation: 'riskyOperation' },
});
}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();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>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')}
/>
);
}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.
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.
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.
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.
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.
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.
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();MIT