What This Helper Is
navigation.zoom() is the high-level bounds helper for navigation-driven zoom transitions.
It sits on top of the same boundary link system as bounds(), but instead of manually composing slot output yourself, it builds the main layer map for you.
Use it when you want a source-to-destination handoff that feels like a navigation zoom without building the transition from raw bounds primitives.
Recommended Setup
Mark the source with Boundary.Trigger
Always use
Transition.Boundary.Trigger on the source side.Boundary.Trigger is the recommended owner because it captures source bounds before your navigation callback runs.Narrow measurement with Boundary.Target when needed
If the pressable owner is larger than the visual element you actually want to measure, keep the trigger as the owner and move measurement to a nested
Transition.Boundary.Target.Boundary.Target does not define its own id. It inherits the surrounding boundary owner and only changes which descendant gets measured.Run navigation.zoom() in the destination screen options
Put the helper inside the destination screen’s The id does not have to come from route params. You can resolve it from route params, a Reanimated shared value created with
screenStyleInterpolator.makeMutable, Zustand-backed state, or any other source, as long as the interpolator can read the active boundary id.Destination Setup
- Source Only
- Matched Destination
The destination screen does not need a
Transition.Boundary.View for navigation.zoom() to work.If all you need is a source-driven navigation zoom into the detail screen, the source Boundary.Trigger plus the interpolator lookup is enough.This is the simpler setup and is often all you need for id-based list-to-detail flows.zoom() Options
navigation.zoom() accepts an optional configuration object:
Controls the destination target for the matched element. Leave it unset for the default navigation zoom behavior. Use
target: "bound" when you want tighter source-to-destination matching. The current 3.4 docs intentionally focus on the default behavior and target: "bound".Shows the helper’s internal layers more explicitly while you tune the transition.
Overrides the destination corner radius used by the helper.
Controls the focused-screen matched element opacity curves.
Controls the unfocused-screen matched element opacity curves.
Scales the unfocused background content while the focused bound animates above it.
Horizontal gesture drag scaling tuple in the form
[shrinkMin, growMax, exponent?].Vertical gesture drag scaling tuple in the form
[shrinkMin, growMax, exponent?].Horizontal gesture drag translation tuple in the form
[negativeMax, positiveMax, exponent?].Vertical gesture drag translation tuple in the form
[negativeMax, positiveMax, exponent?].Opacity tuples use
[inputStart, inputEnd, outputStart?, outputEnd?]. Drag scale tuples use [shrinkMin, growMax, exponent?]. Drag translation tuples use [negativeMax, positiveMax, exponent?].Navigation Mask
navigationMaskEnabled is optional for navigation.zoom() itself.
Enable it when you want masked reveal behavior during the navigation handoff:
@react-native-masked-view/masked-view when you enable navigationMaskEnabled.
Groups
Most navigation zoom flows do not need groups. If each source item navigates to its own destination route and the handoff is simplyid -> route, plain id matching is enough.
Use group when the active matched member can change while the destination flow stays mounted, such as a paged gallery or a retargeted collection detail flow.
id is currently active inside the group so navigation.zoom() knows which member to resolve against.
Performance
The current 3.4 architecture favors correctness over measurement efficiency. Measurement runs through Reanimated, but v3.4 still over-measures in somenavigation.zoom() flows because the entering animation cannot yet be fully postponed until destination measurement is ready.
When destination layout races transition start, the system may measure more than once to recover a usable pair for navigation.zoom(). In practice this is still fast enough for normal use, but you may notice slight stutter toward the end of an animation in heavier cases.
The next major version is expected to change this architecture so the entering animation can be postponed until measurement is ready. That should remove a lot of the repeated measurement work that v3.4 still needs today.
