AI-powered crash analysis is now available on all plans — including Free.Read the crash analysis guide

Expo Managed Workflow: Complete Crash Reporting Setup Guide

NFNourin Mahfuj Finick··8 min read

Expo managed workflow apps trade flexibility for simplicity — you don't touch native code, but you also can't use arbitrary native modules. Crash reporting in a managed workflow app has a few specific constraints you need to know about before you start.


This guide covers everything: installing the SDK in a managed project, configuring EAS Build for source maps, tracking navigation with Expo Router, and verifying your setup works in production.


Managed vs Bare Workflow: What's Different


In a bare workflow app, you run npx pod-install and link native modules directly. In a managed workflow app, Expo manages native code for you — you can only use modules that Expo's managed infrastructure supports.


BugsPulse's React Native SDK works in managed workflow because it uses Expo-compatible native modules. Install with npx expo install (not npm install) to ensure the correct compatible version is resolved:


npx expo install @bugspulse/react-native

No pod-install, no android/ edits needed.


Initialization


Initialize BugsPulse in your root _layout.tsx (Expo Router) or App.tsx (Expo classic):


// app/_layout.tsx (Expo Router)
import BugsPulse from '@bugspulse/react-native';
import { useEffect } from 'react';
import { Stack } from 'expo-router';

BugsPulse.init({
  apiKey: process.env.EXPO_PUBLIC_BUGSPULSE_KEY!,
  environment: process.env.EXPO_PUBLIC_ENV ?? 'production',
  sessionReplay: true,
  captureNetworkRequests: true,
});

export default function RootLayout() {
  return <Stack />;
}

Store your API key in a .env.local file — Expo's config system picks up any EXPO_PUBLIC_ prefixed variable automatically:


# .env.local
EXPO_PUBLIC_BUGSPULSE_KEY=bp_your_project_key
EXPO_PUBLIC_ENV=production

Expo Router Navigation Tracking


Expo Router is file-based routing built on React Navigation. To track screen views as breadcrumbs:


// app/_layout.tsx
import { useNavigationContainerRef } from 'expo-router';
import BugsPulse from '@bugspulse/react-native';
import { useEffect, useRef } from 'react';
import { Stack } from 'expo-router';

export default function RootLayout() {
  const routeNameRef = useRef<string | undefined>();

  return (
    <Stack
      screenOptions={{ headerShown: false }}
      // Track navigation on each route change
    >
      {/* Use Expo Router's built-in navigation events */}
    </Stack>
  );
}

For Expo Router v3+, use the useSegments hook to capture the current route:


import { useSegments, usePathname } from 'expo-router';
import { useEffect } from 'react';
import BugsPulse from '@bugspulse/react-native';

function NavigationTracker() {
  const pathname = usePathname();

  useEffect(() => {
    BugsPulse.setCurrentScreen(pathname);
    BugsPulse.addBreadcrumb({
      message: `Navigated to ${pathname}`,
      category: 'navigation',
    });
  }, [pathname]);

  return null;
}

// Add <NavigationTracker /> inside your RootLayout

EAS Build + Source Maps


Without source maps, production crash stack traces are unreadable. EAS Build can generate them automatically.


eas.json configuration


{
  "build": {
    "production": {
      "env": {
        "EXPO_PUBLIC_ENV": "production"
      },
      "android": {
        "buildType": "apk",
        "gradleCommand": ":app:bundleRelease"
      },
      "ios": {
        "buildConfiguration": "Release"
      }
    }
  }
}

app.config.js — enable source maps


// app.config.js
export default {
  expo: {
    name: 'My App',
    // ...
    extra: {
      eas: {
        projectId: 'your-eas-project-id',
      },
    },
    hooks: {
      postPublish: [
        {
          file: 'sentry-expo/upload-sourcemaps', // replace with BugsPulse equivalent
          config: {},
        },
      ],
    },
  },
};

After a successful EAS Build, download the source maps from the EAS dashboard and upload them:


npx bugspulse-cli upload-sourcemaps   --api-key bp_your_project_key   --version $(cat app.json | jq -r '.expo.version')   --platform android   --bundle .expo/dist/index.android.bundle   --sourcemap .expo/dist/index.android.bundle.map

Testing Your Setup


Before deploying to production, verify crashes are captured in development:


// Temporary test button in a dev screen
import BugsPulse from '@bugspulse/react-native';

<Button
  title="Test crash capture"
  onPress={() => {
    BugsPulse.captureException(new Error('Test exception from Expo'));
  }}
/>

Open your BugsPulse dashboard — the test exception should appear within seconds. Remove the test button before shipping.


Common Managed Workflow Gotchas


`npx expo install` vs `npm install` — Always use npx expo install for native modules. It resolves the version compatible with your current Expo SDK version.


Expo Go doesn't support all native modules — BugsPulse may have limited functionality in Expo Go. Use a development build (npx expo run:ios) for full testing.


Environment variables in EAS — Variables defined in .env.local aren't automatically available in EAS Build. Add production secrets via eas secret:create or in your EAS project settings.


OTA updates (Expo Updates) — If you use Expo's over-the-air updates, upload new source maps with each OTA bundle, not just native builds.


With this setup complete, every crash in your Expo managed workflow app captures the full session context — navigation path, network requests, and symbolicated stack trace — regardless of which device it occurs on.