Skip to content

feat(tracing): Add <Sentry.NavigationContainer> wrapper to auto-register React Navigation #6065

@antonis

Description

@antonis

Problem

reactNavigationIntegration currently requires users to manually register the navigation container ref:

const ref = useRef(null);

<NavigationContainer
  ref={ref}
  onReady={() => navigationIntegration.registerNavigationContainer(ref)}
>
  ...
</NavigationContainer>

This boilerplate is repeated across every @react-navigation/native app and is a recurring source of misconfiguration in bug reports — when users forget the onReady call, navigation transactions never start and they see no spans / "Route Change" placeholders without realising why.

Proposal

Ship a thin wrapper component that renders <NavigationContainer> with the registration wired internally:

<Sentry.NavigationContainer>
  ...
</Sentry.NavigationContainer>
  • Internally renders <NavigationContainer ref={internalRef} onReady={...}> and calls navigationIntegration.registerNavigationContainer(internalRef) on ready.
  • Forwards ref (via forwardRef) so users keep imperative navigation (navigationRef.navigate(...)).
  • Pass-through for all <NavigationContainer> props.
  • Resolves the active reactNavigationIntegration from the current client; no-ops cleanly if it isn't registered.

What the current navigation auto-instrumentation covers

reactNavigationIntegration (@react-navigation/native and Expo Router; packages/core/src/js/tracing/reactnavigation.ts):

  • User wires it up by calling registerNavigationContainer(ref) once after the container mounts (onReady for raw RN, useEffect for Expo Router).
  • Listens to the container's unsafe_action (every dispatch) and state events to start/finish idle navigation spans.
  • Names spans from the current route, with optional full-path naming across nested navigators (useFullPathsForNavigationRoutes) and span-name customization from dispatched action payloads (useDispatchedActionData).
  • Extracts Expo-Router-style dynamic route params ([id], [...slug]) into span attributes, with non-structural query params filtered out for PII safety.
  • Optional TTID start via enableTimeToInitialDisplay (with a fallback when the route doesn't render a component) and enableTimeToInitialDisplayForPreloadedRoutes.
  • Optional prefetch span tracking (enablePrefetchTracking) when used with Sentry.wrapExpoRouter(useRouter()).
  • Drops empty back-navigations (ignoreEmptyBackNavigationTransactions, default on) and route-change transactions that never produce a route.
  • Re-registration is supported for Android Activity recreate; initial-state handling creates a span for the very first route even when Sentry.init runs before the container mounts, falling back to "Route Changed" if it doesn't.

reactNativeNavigationIntegration (Wix react-native-navigation; packages/core/src/js/tracing/reactnativenavigation.ts):

  • Auto-wires by passing navigation: Navigation to the integration at Sentry.init — no per-container registration call.
  • Starts navigation spans on componentDidAppear events from RNN.

Tied in but separate:

  • Sentry.wrap(App) — App Start completion + touch event boundary; not navigation-specific.
  • Sentry.wrapExpoRouter(useRouter()) — adds prefetch spans for Expo Router v5+; doesn't replace registerNavigationContainer.

References

https://docs.sentry.io/platforms/react-native/tracing/instrumentation/automatic-instrumentation/
https://docs.sentry.io/platforms/react-native/tracing/instrumentation/react-native-navigation/

Metadata

Metadata

Assignees

No one assigned
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions