react-native-bug-reporter
Universal bug reporting for any React Native app. Detect screenshots, annotate them, capture crashes + diagnostics, and POST everything to your Bug Reporter HTTP API with one API key.
When a user takes a screenshot anywhere in your app, a report modal pops up with the screenshot pre-attached. The user can mark the bug (pen/arrow/point/text), adds a title/description/severity; the SDK auto-collects user, device, app, current screen, network, timestamp and uploads everything to your API.
- Automatic screenshot detection (native iOS + Android)
- Annotation editor — pen, arrow, point, text, hide/redact sensitive areas
- Auto JS crash capture — uncaught errors file a report automatically (with crash location + breadcrumbs)
- Diagnostics — recent console logs + network requests + breadcrumbs attached to every report
- Category + tags on reports
- One-component install —
<BugReporterProvider> - Auto-collected context (device, app, user, screen, network, time)
- Any HTTP backend — single multipart
POST /api/v1/bugswithX-Api-Key - Themeable, TypeScript-first, autolinked
1. Install
npm install react-native-bug-reporter \
@react-native-community/netinfo react-native-device-info \
react-native-svg react-native-view-shot
cd ios && pod install && cd ..These are native modules — rebuild the app after installing. The networking
to your API is plain fetch (no extra config).
2. Wrap your app
import { BugReporterProvider } from 'react-native-bug-reporter';
export default function App() {
return (
<BugReporterProvider
config={{
baseUrl: 'https://bugsreporter.example.com',
apiKey: 'brk_xxxxxxxxxxxxxxxx',
appKey: 'my-app', // multi-app id
notifyEmails: 'admin@yourcompany.com', // who gets the email
environment: __DEV__ ? 'development' : 'production',
getUser: () => ({ id: user.id, name: user.name, email: user.email }),
}}
>
<RootNavigator />
</BugReporterProvider>
);
}Now take a screenshot in the running app — the report modal appears with the screenshot attached.
4. Full functionality
Trigger the modal manually
import { useBugReporter } from 'react-native-bug-reporter';
function HelpScreen() {
const { openReporter } = useBugReporter();
return <Button title="Report a bug" onPress={() => openReporter()} />;
}useBugReporter() returns:
| Field | Type | Description |
|---|---|---|
openReporter |
(screenshot?) => void |
Open the modal (optionally pre-attach an image). |
closeReporter |
() => void |
Close it. |
isOpen |
boolean |
Modal visibility. |
isAutoDetectEnabled |
boolean |
Whether screenshot detection is on. |
Track the current screen
The SDK is navigation-agnostic. Report the active route so it's attached to bug reports. With React Navigation:
import {
setCurrentScreen,
getActiveRouteName,
} from 'react-native-bug-reporter';
<NavigationContainer
onStateChange={state => {
const route = getActiveRouteName(state);
if (route) setCurrentScreen(route);
}}
/>;…then pass getCurrentScreen in the config:
import { getCurrentScreen } from 'react-native-bug-reporter';
// config: { ..., getCurrentScreen }Or just call setCurrentScreen('Checkout') from any screen's effect.
Multi-app support
Give each app its own appKey (and appName) — every report is tagged with it,
so your backend/admin can show per-app sections and route emails per app:
<BugReporterProvider config={{ baseUrl, apiKey, appKey: 'shop', appName: 'Shop App' }}>Reports are viewed/managed in your hosted admin panel (the backend), not in the app.
API contract
The SDK sends one multipart/form-data request:
POST {baseUrl}/api/v1/bugs
X-Api-Key: <apiKey>
payload = JSON { appKey, title, description, severity, status, createdAt,
category, tags, notifyEmails, context }
screenshots[] = image file(s)
context includes user, device, app, network, currentScreen, timestamp and a
diagnostics object (recent logs, network, errors with crash location, and
breadcrumbs). The server stores the report + screenshots and emails
notifyEmails.
Theming
config={{
baseUrl, apiKey,
theme: {
primaryColor: '#7c3aed',
backgroundColor: '#ffffff',
textColor: '#111827',
mutedColor: '#6b7280',
borderColor: '#e5e7eb',
errorColor: '#ef4444',
},
}}Callbacks & options
config={{
baseUrl, apiKey,
disableAutoDetect: false, // set true for manual-only reporting
debug: __DEV__, // verbose logging
onSubmitted: (report) => analytics.track('bug_reported', { id: report.id }),
onError: (err) => console.warn(err),
}}Config reference (BugReporterConfig)
| Option | Type | Default | Description |
|---|---|---|---|
baseUrl |
string (required) |
— | Bug Reporter API base URL. |
apiKey |
string (required) |
— | Sent as the X-Api-Key header. |
appKey |
string |
default |
Multi-app id — tags every report so your backend can group per app. |
appName |
string |
appKey |
Human-friendly app name. |
notifyEmails |
string | string[] |
— | Recipient(s) the server emails for this report. |
captureCrashes |
boolean |
true |
Auto-capture uncaught JS errors as crash reports. |
captureConsoleLogs |
boolean |
true |
Attach recent console logs. |
captureNetwork |
boolean |
true |
Attach recent network requests. |
categories |
string[] |
preset list | Category options in the modal. |
getUser |
() => UserInfo | Promise<…> |
— | Current signed-in user. |
getCurrentScreen |
() => string | null |
— | Active route name. |
environment |
string |
— | e.g. "production". |
showContextSummary |
boolean |
true |
Show the "Automatically attached" block (data is always collected). |
disableAutoDetect |
boolean |
false |
Manual-only reporting. |
theme |
BugReporterTheme |
light theme | Color overrides. |
onSubmitted |
(report) => void |
— | Success callback. |
onError |
(error) => void |
— | Failure callback. |
debug |
boolean |
false |
Verbose logging. |
How screenshot capture works
Instead of reading the device Photos library (permission-heavy, may miss the frame), the native modules snapshot the current app window the instant a screenshot is detected:
- iOS —
userDidTakeScreenshotNotification→ render the key window → PNG. - Android 14+ —
Activity.registerScreenCaptureCallback→PixelCopy→ PNG. - Android ≤13 —
MediaStoreContentObserver→PixelCopy→ PNG.
The JS layer shows the image in the modal and streams it to your backend as
multipart/form-data.
The build emits CommonJS, ESM, and TypeScript declarations to lib/.
License
MIT