Skip to content

fix(metro): Dual ESM/CJS bundling of @sentry/core on RN 0.79+ and unnecessary server integrations in native bundles #6262

Description

@antonis

Description

Since React Native 0.79, Metro enables unstable_enablePackageExports by default (Metro 0.82+). @sentry/core's exports field only has import and require conditions — no react-native condition. Metro misclassifies some ESM imports as isESMImport=false, causing both the ESM and CJS module trees of @sentry/core to be bundled into native apps.

Impact

  • ~1 MB of duplicate CJS code ships in every RN 0.79+ app (Hermes compiles everything — no dead code elimination)
  • Server-only integrations from @sentry/core (Express, PostgresJS, Consola, requestdata, spanStreaming) are also bundled unnecessarily (~100 KB)
  • Affects the final APK/IPA shipped to stores

Root cause

Metro with enablePackageExports resolves @sentry/core using conditional exports. For most imports, the import condition is used → ESM. But three specific origins get isESMImport=false due to a Metro detection issue:

  • @sentry/browser/.../contextlines.js
  • @sentry-internal/feedback/.../index.js
  • @sentry-internal/replay/.../index.js

These files use ESM import syntax, but Metro classifies them as CJS require. This triggers the require export condition → CJS resolution → all 169 internal CJS files get pulled in alongside the 169 ESM files.

Verified bundle analysis (sample app, Android)

@sentry/core filesBundle size
Before fix338 CJS + 338 ESM = 6765.09 MB
After fix0 CJS + 314 ESM = 3144.52 MB

Fix

Two complementary fixes:

  1. This repo (Metro config): Add withSentryEsmResolver to redirect CJS → ESM resolutions for @sentry/core on native platforms. Expand SERVER_ONLY_MODULE_RE to also exclude Express, PostgresJS, Consola, requestdata, and spanStreaming integrations.

  2. Upstream (sentry-javascript): Add "react-native" export condition to @sentry/core, @sentry/browser, and @sentry/react package.json, pointing to ESM builds. This is the long-term fix that works even without withSentryConfig(). See: getsentry/sentry-javascript#XXXX

Timeline

  • @sentry/core v8.0 (May 2024): added conditional exports with import/require
  • RN 0.79 (April 2025): Metro enabled unstable_enablePackageExports by default → dual bundling started
  • RN < 0.79: not affected (Metro ignores exports field)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions