# Apple Zoom Transitions Fluid zoom transitions for navigating between screens. iOS 18+, Expo SDK 55+, Stack navigator only. ```tsx import { Link } from "expo-router"; ``` ## Basic Zoom Use `withAppleZoom` on `Link.Trigger` to zoom the entire trigger element into the destination screen: ```tsx ``` ## Targeted Zoom with `Link.AppleZoom` Wrap only the element that should animate. Siblings outside `Link.AppleZoom` are not part of the transition: ```tsx Caption text (not zoomed) ``` `Link.AppleZoom` accepts only a single child element. ## Destination Target Use `Link.AppleZoomTarget` on the destination screen to align the zoom animation to a specific element: ```tsx // Destination screen (e.g., app/photo.tsx) import { Link } from "expo-router"; export default function PhotoScreen() { return ( Photo details below ); } ``` Without a target, the zoom animates to fill the entire destination screen. ## Custom Alignment Rectangle For manual control over where the zoom lands on the destination, use `alignmentRect` instead of `Link.AppleZoomTarget`: ```tsx ``` Coordinates are in the destination screen's coordinate space. Prefer `Link.AppleZoomTarget` when possible — use `alignmentRect` only when the target element isn't available as a React component. ## Controlling Dismissal Zoom screens support interactive dismissal gestures by default (pinch, swipe down when scrolled to top, swipe from leading edge). Use `usePreventZoomTransitionDismissal` on the destination screen to control this. ### Disable all dismissal gestures ```tsx import { usePreventZoomTransitionDismissal } from "expo-router"; export default function PhotoScreen() { usePreventZoomTransitionDismissal(); return ; } ``` ### Restrict dismissal to a specific area Use `unstable_dismissalBoundsRect` to prevent conflicts with scrollable content: ```tsx usePreventZoomTransitionDismissal({ unstable_dismissalBoundsRect: { minX: 0, minY: 0, maxX: 300, maxY: 300, }, }); ``` This is useful when the destination contains a zoomable scroll view — the system gives that scroll view precedence over the dismiss gesture. ## Combining with Link.Preview Zoom transitions work alongside long-press previews: ```tsx ``` ## Best Practices **Good use cases:** - Thumbnail → full image (gallery, profile photos) - Card → detail screen with similar visual content - Source and destination with similar aspect ratios **Avoid:** - Skinny full-width list rows as zoom sources — the transition looks unnatural - Mismatched aspect ratios between source and destination without `alignmentRect` - Using zoom with sheets or popovers — only works in Stack navigator - Hiding the navigation bar — known issues with header visibility during transitions **Tips:** - Always provide a close or back button — dismissal gestures are not discoverable - If the destination has a zoomable scroll view, use `unstable_dismissalBoundsRect` to avoid gesture conflicts - Source view doesn't need to match the tap target — only the `Link.AppleZoom` wrapped element animates - When source is unavailable (e.g., scrolled off screen), the transition zooms from the center of the screen ## References - Expo Router Zoom Transitions: https://docs.expo.dev/router/advanced/zoom-transition/ - Link.AppleZoom API: https://docs.expo.dev/versions/v55.0.0/sdk/router/#linkapplezoom - Apple UIKit Fluid Transitions: https://developer.apple.com/documentation/uikit/enhancing-your-app-with-fluid-transitions