Changelogs

New in 3.13

v3.13 makes Boundary transitions more predictable and reduces animation work in deep retained stacks.

v3.13 makes bounds more global and retained stacks less expensive. Matching Boundaries can now coordinate across complex navigation structures without depending on where each Boundary happens to mount, while stackProgress no longer requires a navigator-wide update on every animation frame.

The result is more predictable transitions across deeply nested navigators, repeated route pushes, dynamic Boundary IDs, and flows where only one side of a bounds transition exists.

Boundaries across complex navigation

Matching Boundary IDs can now find each other across nested layouts and navigators without extra setup. This includes cases where the source and destination live several navigator levels apart or where the same ID is reused across repeated pushes of the same route.

Source-only transitions, fullscreen and custom targets, and destinations that appear before their source are also handled as normal bounds flows. A matching destination is no longer required before a source can participate.

Bounds transitions also account more reliably for scale and position styles applied directly to a Boundary. Forward and backward transitions refresh their endpoints before animating, keeping them aligned when a Boundary has moved, resized, or changed scale since the previous transition.

Escaped Boundary portals and live handoffs follow the same behavior. Content remains with its current screen until the next destination is ready, reducing flicker during multi-screen push and pop sequences.

Less animation work in deep stacks

stackProgress is still the accumulated visual progress from the current screen through the top of its navigator. Its public value and usage have not changed.

Internally, the navigator previously recalculated and wrote stackProgress for every retained route whenever visual progress changed. v3.13 removes that navigator-wide reaction. A live interpolation frame now derives its value directly from the relevant route suffix, so the top screen reads one progress value and deeper consumers read only as far as their own route.

In matched release-mode profiling on a Pixel 8 at retained depths of 1, 26, and 51, samples attributed to animation-frame work fell from 927 to 626, a 32.5% reduction, while total sampled CPU fell by 4.9%. Hermes time and native commit counts remained essentially flat. This isolates a real animation-thread improvement, but it does not remove the normal memory, layout, and native-tree cost of retaining more screens.

More reliable inactive-screen settlement

Inactive presentation now waits for the paint-driving screen's visual progress to settle. This prevents an older screen from being hidden during the final visual frames of a nested transition even when its underlying transition clock has already completed.

After safe paint, blank-stack screens using inactiveBehavior: "hide" or "pause" now freeze their retained React subtree when native screens are enabled. hide also uses display: "none" instead of moving the screen far outside the viewport. The screen remains mounted and can thaw with its local state intact; use "unmount" when an eligible subtree and its resources should actually be released.

This freezing behavior is provided by react-native-screens. With nativeScreens={false}, hidden screens still use display: "none", but the regular React Native view does not provide the same native-screen freeze behavior.

More predictable locked snap gestures

gestureSnapLocked now keeps animatedSnapIndex pinned to the current detent while dragging, dismissing, and settling back after a cancelled gesture. A programmatic snapTo() issued during that settlement can still take over and animate to its requested snap point.

Cancelled gestures now return to their starting snap point without carrying release velocity into the reset. Swipes that commit to a different snap point keep their velocity, preserving natural momentum between detents while keeping snap-backs soft and controlled.

Upgrade notes

No Boundary or stackProgress API migration is required. Existing id, group, handoff, escapeClipping, .styles(), .values(), .link(), navigation zoom, reveal, and interpolator APIs retain the same shape.

The experimental_updateInactiveInterpolators option included in 3.13.0-beta.0 was removed before the final release. Remove it if you tested that beta; there is no replacement option to configure.

The hidden-screen gesture reattachment path was verified with React Native Gesture Handler 2.31.1. The package's peer range has not changed, but 2.31.1 is the known-good version to use if gestures on a hidden screen do not respond after it becomes active again.