-
Notifications
You must be signed in to change notification settings - Fork 55
Description
Description
For cross-platform React Native apps that also target web (e.g., Expo with web support), the v3 SDK's build tooling plugins assume a native-only build pipeline.
Both withDatadogMetroConfig and @datadog/mobile-react-native-babel-plugin run unconditionally for all platforms, requiring consumers to manually add platform guards in their build configs.
Ideally the tools could add built-in platform awareness, so cross-platform apps don't need custom conditioning boilerplate.
Current Workarounds
babel.config.js — skip babel plugin for web
module.exports = function (api) {
const isWeb = api.caller((caller) => caller && caller.platform === 'web');
api.cache.using(() => `platform:${isWeb ? 'web' : 'native'}`); // api.cache.using() lets Babel cache per-platform instead just a single case
const plugins = [ 'react-native-reanimated/plugin', /* ...other plugins... */ ];
// Only add for native builds
if (!isWeb) plugins.push('@datadog/mobile-react-native-babel-plugin');
return {
presets: ['babel-preset-expo'],
plugins,
};
};metro.config.js — skip metro plugin for web
const { withDatadogMetroConfig } = require('@datadog/mobile-react-native/metro');
// ...config setup...
const isWebBuild = process.env.EXPO_PUBLIC_PLATFORM === 'web';
module.exports = isWebBuild ? baseConfig : withDatadogMetroConfig(baseConfig);These work, but every cross-platform consumer has to independently figure out and maintain this conditioning.
🦙 Analysis + Recommendations
withDatadogMetroConfig
Analysis — no platform check
The custom serializer in metroSerializer.js only checks graph.transformOptions.hot (to skip hot reload). It never checks graph.transformOptions.platform, which Metro provides for every build and will be "web" for web bundles.
What happens without conditioning:
- The serializer injects a debug ID virtual module and rewrites the bundle + sourcemap for every platform, including web.
- Web builds bundled through Metro (Expo web) will get unnecessary debug ID injection. The injected
globalThis.__dd_debug_idcode is dead weight — web sourcemaps are handled by@datadog/browser-rumvia a completely separate mechanism (Datadog CIsourcemaps uploador browser SDK inline). - More critically, the serializer calls into Metro internals (
baseJSBundle,bundleToString,sourceMapString) which may behave differently or break for web bundles depending on Metro version and Expo's web pipeline config.
For comparison: Sentry's withSentryConfig (which DD's metro code is adapted from) does have platform-aware logic — it uses graph.transformOptions.platform to conditionally exclude native-only packages from web builds and apply web-specific features like injectReleaseForWeb.
Proposed solution
Option A — Check platform in the serializer (preferred):
The serializer receives graph.transformOptions.platform for every build. It could skip debug ID injection for web:
if (graph.transformOptions.hot || graph.transformOptions.platform === 'web') {
return serializer(entryPoint, preModules, graph, options);
}Option B — Accept a platforms option:
withDatadogMetroConfig(baseConfig, { platforms: ['ios', 'android'] });Option A is better because it requires no consumer changes and "just works."
@datadog/mobile-react-native-babel-plugin
Analysis — platform-aware but doesn't skip web
The plugin already reads caller.platform via PluginState.getPlatform():
getPlatform() {
return this.state?.file?.opts?.caller?.platform || 'unknown';
}But it only uses this for per-platform deduplication of the globalThis.__DD_RN_BABEL_PLUGIN_ENABLED__ flag (so it initializes once per platform). It does not use it to skip web transforms entirely.
What happens without conditioning:
- The plugin instruments every JSX component with RUM action tracking wrappers (
DdBabelInteractionTracking) — even in web bundles. - It injects imports from
@datadog/mobile-react-native/interaction-trackinginto instrumented files. On web,@datadog/mobile-react-nativemay pull in native modules (expo-datadog, React Native bridge code) that don't exist in a browser context. Tree-shaking may or may not save you depending on side effects. - It sets
globalThis.__DD_RN_BABEL_PLUGIN_ENABLED__ = truein web bundles. While@datadog/browser-rumdoesn't check this flag (it's only consumed by@datadog/mobile-react-native), it's still dead code in the wrong context. - The injected
__ddExtractTexthelper andjsx/jsxsruntime wrappers add bundle size overhead for zero benefit on web.
Proposed solution
The plugin already has caller.platform available via PluginState.getPlatform(). It could skip all transforms when platform === 'web':
// In the Program.enter visitor:
if (pluginState.getPlatform() === 'web') {
return; // No-op for web builds
}This is a minimal, backwards-compatible change. The platform detection pattern is well-established — react-native-reanimated/plugin and others use api.caller() the same way.
Environment
@datadog/mobile-react-native: 3.1.2@datadog/mobile-react-native-babel-plugin: 3.1.2@datadog/browser-rum: 6.30.1 (used for web RUM)- Expo SDK: 54
- Metro (via
@expo/metro-config) - Build setup: Nx monorepo with shared Metro/Babel configs serving both native and web targets
Reproduction steps
Steps to reproduce:
Create an Expo app that targets both native (iOS/Android) and web
Install @datadog/mobile-react-native and @datadog/mobile-react-native-babel-plugin
Add withDatadogMetroConfig(config) to metro.config.js without platform conditioning
Add '@datadog/mobile-react-native-babel-plugin' to babel.config.js plugins without platform conditioning
Run npx expo start --web or npx expo export --platform web
Expected: Build tooling is a no-op for web bundles (debug ID injection and RUM action instrumentation are native-only concerns)
Actual:
Metro serializer injects debug IDs into web bundles unnecessarily
Babel plugin instruments web JSX with native RUM action wrappers and injects imports from @datadog/mobile-react-native/interaction-tracking
globalThis.DD_RN_BABEL_PLUGIN_ENABLED is set in web context
SDK logs
No response
Expected behavior
No response
Affected SDK versions
v3
Latest working SDK version
n/a (new features)
Did you confirm if the latest SDK version fixes the bug?
Yes
Integration Methods
NPM
React Native Version
0.81.5
Package.json Contents
No response
iOS Setup
No response
Android Setup
No response
Device Information
No response
Other relevant information
No response