@bbearai/react-native
v0.6.3
Published
BugBear React Native components for mobile apps
Maintainers
Readme
@bbearai/react-native
BugBear QA widget for React Native mobile apps. Features a navigation-stack-based UI with 10 dedicated screens for testing, bug reporting, messaging, and profile management.
Installation
npm install @bbearai/react-native @bbearai/core
# or
yarn add @bbearai/react-native @bbearai/coreOptional: Image attachments
For image attachments in bug reports and messages:
npm install react-native-image-pickerThis is an optional peer dependency — the widget works without it but won't show image picker buttons.
Quick Start
import { BugBearProvider, BugBearButton } from '@bbearai/react-native';
function App() {
return (
<BugBearProvider
config={{
projectId: 'your-project-id',
dashboardUrl: 'https://app.bugbear.ai',
getCurrentUser: async () => ({
id: user.id,
email: user.email,
name: user.name, // Optional, shown on reports
}),
}}
>
<YourApp />
<BugBearButton position="bottom-right" />
</BugBearProvider>
);
}Configuration Options
<BugBearProvider
config={{
// Required
projectId: 'your-project-id',
getCurrentUser: async () => ({ id: user.id, email: user.email }),
// Optional — rich context for bug reports
getAppContext: () => ({
currentRoute: currentScreenName, // Current screen name
userRole: currentUser?.role, // 'owner', 'manager', 'guest', etc.
propertyId: selectedProperty?.id, // App-specific context
custom: { theme: 'dark' }, // Any additional data
}),
// Optional — callbacks
dashboardUrl: 'https://app.bugbear.ai', // Web dashboard link in widget
onNavigate: (route) => navigation.navigate(route), // Deep link handler for test cases
onReportSubmitted: (report) => { ... }, // After report submission
getNavigationHistory: () => [...recentScreens], // Custom nav tracking
// Optional — self-hosted
supabaseUrl: '...',
supabaseAnonKey: '...',
}}
enabled={isAuthReady} // Delay init until auth is ready (default: true)
>Why use getAppContext?
When a tester reports a bug, BugBear attaches the app context to the report. This tells the developer fixing the bug:
- Which screen the bug is on
- What role the user has (critical for role-dependent bugs)
- App-specific state like selected property, active filters, etc.
The bug report form also includes a "Which screen?" field so testers can specify the affected screen manually.
Automatic Context Capture
BugBearProvider automatically captures debugging context with zero configuration:
| Data | Details |
|------|---------|
| Console logs | Last 50 console.log/warn/error/info calls |
| Network requests | Last 20 fetch() calls with method, URL, status, duration |
| Failed response bodies | First 500 chars of 4xx/5xx response bodies |
| Performance | Page load time, memory usage |
| Environment | Language, timezone, online status |
This data is attached to every bug report and available to the MCP server's get_report_context tool for AI-assisted debugging.
Note: For React Native, navigation history is not auto-captured (no
pushState). UsegetNavigationHistoryor React Navigation integration below for screen tracking.
Navigation Tracking
React Navigation integration
import { useNavigationContainerRef } from '@react-navigation/native';
function App() {
const navigationRef = useNavigationContainerRef();
const routeHistory = useRef<string[]>([]);
return (
<BugBearProvider
config={{
projectId: 'your-project-id',
getCurrentUser: async () => ({ id: user.id, email: user.email }),
getNavigationHistory: () => routeHistory.current,
getAppContext: () => ({
currentRoute: navigationRef.getCurrentRoute()?.name || 'unknown',
userRole: currentUser?.role,
}),
}}
>
<NavigationContainer
ref={navigationRef}
onStateChange={() => {
const name = navigationRef.getCurrentRoute()?.name;
if (name) {
routeHistory.current.push(name);
// Keep last 20
if (routeHistory.current.length > 20) routeHistory.current.shift();
}
}}
>
{/* Your navigation */}
</NavigationContainer>
<BugBearButton />
</BugBearProvider>
);
}Widget Architecture
The widget uses a navigation stack pattern with 12 screens:
| Screen | Purpose | |--------|---------| | Home | Smart hero banner + 2x2 action grid + issue tracking cards (Open, Done, Reopened) + progress bar | | Test List | All assignments with filter tabs (All, To Do, Done, Re Opened) and colored status badges | | Test Detail | One-test-at-a-time execution with pass/fail/skip actions | | Test Feedback | Star rating + quality flags after pass/fail | | Report | Bug/feedback submission with type, severity, description, affected screen | | Report Success | Confirmation with auto-return to home | | Issue List | Issues filtered by category (open/done/reopened) with severity indicators | | Issue Detail | Full issue details with verification proof and original bug context | | Message List | Thread list with unread badges + compose button | | Thread Detail | Chat bubbles + reply composer | | Compose Message | New thread form with subject + message | | Profile | Tester info, stats, and editable fields |
Button Configuration
The BugBear button is draggable by default with edge-snapping behavior:
<BugBearButton
draggable={true} // Enable/disable dragging (default: true)
position="bottom-right" // Initial position: 'bottom-right' | 'bottom-left'
initialX={100} // Custom initial X position (optional)
initialY={500} // Custom initial Y position (optional)
minY={100} // Minimum Y from top (default: 100)
maxYOffset={160} // Max Y offset from bottom (default: 160)
/>The button automatically snaps to the nearest screen edge when released.
Monorepo Setup
When using BugBear in a monorepo (e.g., with Turborepo, Nx, or Yarn Workspaces), you may encounter React duplicate instance errors like:
- "Invalid hook call"
- "Cannot read property 'useState' of null"
- "Hooks can only be called inside the body of a function component"
This happens when Metro (React Native's bundler) picks up different React instances from different node_modules folders.
Fix: Configure Metro to use a single React instance
Update your metro.config.js in the React Native app:
const path = require('path');
const { getDefaultConfig } = require('@react-native/metro-config');
// Get the monorepo root (adjust path as needed)
const monorepoRoot = path.resolve(__dirname, '../..');
const config = getDefaultConfig(__dirname);
// Block React from monorepo root to prevent duplicates
config.resolver.blockList = [
...(config.resolver.blockList || []),
new RegExp(`${monorepoRoot}/node_modules/react/.*`),
new RegExp(`${monorepoRoot}/node_modules/react-native/.*`),
];
// Ensure Metro uses the app's React instance
config.resolver.extraNodeModules = {
react: path.resolve(__dirname, 'node_modules/react'),
'react-native': path.resolve(__dirname, 'node_modules/react-native'),
};
// Watch the monorepo packages
config.watchFolders = [
monorepoRoot,
];
module.exports = config;Alternative: Yarn/npm Workspaces hoisting
If you're using Yarn workspaces, you can also configure hoisting to ensure a single React version:
// package.json (workspace root)
{
"workspaces": {
"packages": ["apps/*", "packages/*"],
"nohoist": [
"**/react-native",
"**/react-native/**"
]
}
}Troubleshooting
Widget not showing
- Make sure the current user's email is registered as a tester in the BugBear dashboard
- Verify QA mode is enabled for your project
- Check that
getCurrentUserreturns a valid email
Supabase connection issues
If you're using your own Supabase instance, provide the credentials:
<BugBearProvider
config={{
projectId: 'your-project-id',
supabaseUrl: 'https://your-instance.supabase.co',
supabaseAnonKey: 'your-anon-key',
getCurrentUser: async () => ({ id: user.id, email: user.email }),
}}
>Known Limitations
Button hidden behind modals
The BugBear button renders within the normal React Native view hierarchy using absolute positioning. When your app displays modals, bottom sheets, or the keyboard, the button may be hidden behind them.
Workarounds:
- Accept that the button won't be accessible when modals are open
- Consider dismissing the modal to access BugBear
- For testing flows that involve modals, provide an alternative way to trigger the BugBear widget
License
MIT
