Install the React Native and Expo SDK
Add @bugmojo/react-native so users can shake their phone to report a bug, with console and network data redacted on the device before upload.
@bugmojo/react-native is the official BugMojo SDK for React Native and Expo. It is Expo-first and mostly JavaScript, and needs no native code of your own. It uses the same redaction engine and the same ingest endpoint as the BugMojo web widget, so mobile bugs land next to your web bugs, already redacted and gated by the same remote config.
Package: @bugmojo/react-native on npm · Full reference: the README on GitHub.
Requirements
- Expo SDK 50+ (
expo >= 50), or bare React Native 0.73+ - React 18+
- Node 18+, for
expo prebuildto run the config plugin
expo-device, expo-constants, expo-sensors and react-native-view-shot are optional peer dependencies. A missing module only switches its own feature off. Without expo-sensors, for example, there is no shake gesture.
Install
# Expo (recommended: installs compatible versions)
npx expo install @bugmojo/react-native expo-device expo-constants expo-sensors react-native-view-shot# or with a plain package manager
pnpm add @bugmojo/react-native
# or: npm install @bugmojo/react-native
# or: yarn add @bugmojo/react-nativeAdd the Expo config plugin
{
"expo": {
"plugins": [
["@bugmojo/react-native", { "motionUsageDescription": "Shake to report a bug." }]
]
}
}Then run npx expo prebuild, or build with EAS. The plugin sets the iOS NSMotionUsageDescription that shake-to-report needs, merges the SDK's privacy-manifest entries into expo.ios.privacyManifests, and adds the Android HIGH_SAMPLING_RATE_SENSORS permission (a normal, non-dangerous permission on Android 12+).
Find your project token
projectId is your project's public embed token, the same data-project token the web widget uses. Copy it from Project Settings → Widget in the BugMojo dashboard at www.bugmojo.com. It is not a secret.
Unlike the React SDK, this SDK requires apiBase. For the hosted platform, set it to https://www.bugmojo.com.
Wrap your app in the provider
import { BugMojoProvider, useBugMojo } from '@bugmojo/react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as ReactNative from 'react-native';
import * as Sensors from 'expo-sensors';
import * as Device from 'expo-device';
import Constants from 'expo-constants';
export default function App() {
return (
<BugMojoProvider
projectId="YOUR_EMBED_TOKEN" // PUBLIC data-project token (not a secret)
apiBase="https://www.bugmojo.com" // where BugMojo is served
env="production"
release="1.4.2"
store={AsyncStorage} // persists the last-good config cache
// Inject the optional modules you installed — Metro/Hermes release builds cannot
// resolve modules by name at runtime, so this is the reliable path.
nativeModules={{
sensors: Sensors,
device: Device,
constants: Constants,
reactNative: ReactNative,
}}
>
<RootNavigator />
</BugMojoProvider>
);
}Report a bug
Shake the device to open the built-in report modal. Use a physical device, because most simulators have no accelerometer. You can also file a report from code:
function PayButton() {
const { report, identify, track } = useBugMojo();
identify({ id: user.id, email: user.email });
track('checkout_started', { cartId });
// ...
await report({ title: 'Payment button unresponsive' });
}report() takes { title, description?, category?, reporterEmail?, context? } and always resolves with { ok, number?, pendingModeration?, error? }. track() records a breadcrumb (the last 50 are attached to reports), and open() / close() drive the built-in modal if you want your own button as well.
Catch crashes
Crash capture is on by default. An uncaught JS error anywhere in the app, not only inside a React subtree, auto-files a report carrying the console, network and masked frames from before the crash, tagged bugmojo_crash: true. Repeat errors are deduplicated by signature and capped at maxCrashReports (default 3) per session. For render errors in a subtree, add the error boundary, which also attaches the React component stack:
import { BugMojoErrorBoundary } from '@bugmojo/react-native';
<BugMojoErrorBoundary fallback={({ error, reset }) => <Crash onRetry={reset} />}>
<Screen />
</BugMojoErrorBoundary>Privacy and masking
Redaction runs on the device, before upload. Emails, card numbers (Luhn-gated), SSNs, phone numbers, auth headers, JWTs and provider secrets (AWS, Stripe, GitHub, Slack, Google, PEM) are scrubbed from console and network data. Sensitive URL query parameters are masked and fragments are dropped, and network bodies are off by default. Capture only runs when your project's capture ruleset is enabled, the session passes sampling and the user's cohort matches. If remote config is unreachable for more than 24 hours, capture turns off.
import { BugMojoMask, BugMojoInput } from '@bugmojo/react-native';
<BugMojoMask label="Card number">
<Text>{card.number}</Text>
</BugMojoMask>
<BugMojoInput testID="card-number" value={card.number} onChangeText={setCard} />More features
The package README covers the optional features in depth: a masked screenshot filmstrip with tunable cadence, a touch trail of taps and swipes, navigation breadcrumbs from React Navigation or Expo Router, a headless client (createBugMojoClient) for code outside React, and Test Mode for running assigned test cases from a tester build. See the full README on GitHub.
Common questions
Why doesn't shaking the device open the report modal?
Check, in order: expo-sensors is installed and passed in nativeModules (release builds cannot find it otherwise); the config plugin ran (npx expo prebuild) so iOS has NSMotionUsageDescription; you are shaking a physical device, since most simulators have no accelerometer; and enableShakeToReport is not set to false. useBugMojo().report() and open() always work as a fallback.
Why do my reports have no screenshots?
Screenshot replay needs all four: react-native-view-shot installed and injected as nativeModules.viewShot, a screenshotRef, and both enableScreenshotReplay and acknowledgeScreenshotRisk. Frames are also skipped on purpose when a mask cannot confirm its overlay committed in time.
Why do my reports have no console or network logs?
Passive capture only installs when the capture gate passes: your project's ruleset is enabled, the session falls inside the sample rate, and the user's cohort matches. Check useBugMojo().capturing; if it is false, review the capture ruleset in your BugMojo project settings.
Does it record video or use rrweb?
No. There is no DOM on native, so replay is a rolling buffer of masked screenshots. rrweb is never bundled into this SDK.
Do I need to eject or write native code?
No. The native parts come from the optional Expo modules plus the config plugin, and expo prebuild or EAS Build handles the rest. Bare React Native apps work too: set the Info.plist key and the privacy manifest yourself.
Related articles
- Install the React SDK for React and Next.jsAdd @bugmojo/react to mount the BugMojo widget, catch render errors with their component stack, and file bugs straight from your code.
- Embed the feedback widget on your websiteAdd one script tag, or the @bugmojo/widget npm package, so visitors can report bugs with a session replay and console and network logs attached.
- Redact PII on the client before it leaves the browserControl what personal data the extension masks, so sensitive text never leaves the browser in a bug report.
- Privacy overview: what BugMojo collectsUnderstand exactly what a bug report captures, where it is stored, and how redaction protects your data.
- Use the BugMojo CLI to reproduce and verify fixesTurn a captured bug into a runnable Playwright repro, verify the fix against a real deploy, and brief your AI agent from your terminal or CI.

