Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Official Expo team skill for building native UI in Expo apps, fine-tuned for Opus models and usable with Claude Code or Cursor.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/zoom-transitions.md
1# Apple Zoom Transitions23Fluid zoom transitions for navigating between screens. iOS 18+, Expo SDK 55+, Stack navigator only.45```tsx6import { Link } from "expo-router";7```89## Basic Zoom1011Use `withAppleZoom` on `Link.Trigger` to zoom the entire trigger element into the destination screen:1213```tsx14<Link href="/photo" asChild>15<Link.Trigger withAppleZoom>16<Pressable>17<Image18source={{ uri: "https://example.com/thumb.jpg" }}19style={{ width: 120, height: 120, borderRadius: 12 }}20/>21</Pressable>22</Link.Trigger>23</Link>24```2526## Targeted Zoom with `Link.AppleZoom`2728Wrap only the element that should animate. Siblings outside `Link.AppleZoom` are not part of the transition:2930```tsx31<Link href="/photo" asChild>32<Link.Trigger>33<Pressable style={{ alignItems: "center" }}>34<Link.AppleZoom>35<Image36source={{ uri: "https://example.com/thumb.jpg" }}37style={{ width: 200, aspectRatio: 4 / 3 }}38/>39</Link.AppleZoom>40<Text>Caption text (not zoomed)</Text>41</Pressable>42</Link.Trigger>43</Link>44```4546`Link.AppleZoom` accepts only a single child element.4748## Destination Target4950Use `Link.AppleZoomTarget` on the destination screen to align the zoom animation to a specific element:5152```tsx53// Destination screen (e.g., app/photo.tsx)54import { Link } from "expo-router";5556export default function PhotoScreen() {57return (58<View style={{ flex: 1 }}>59<Link.AppleZoomTarget>60<Image61source={{ uri: "https://example.com/full.jpg" }}62style={{ width: "100%", aspectRatio: 4 / 3 }}63/>64</Link.AppleZoomTarget>65<Text>Photo details below</Text>66</View>67);68}69```7071Without a target, the zoom animates to fill the entire destination screen.7273## Custom Alignment Rectangle7475For manual control over where the zoom lands on the destination, use `alignmentRect` instead of `Link.AppleZoomTarget`:7677```tsx78<Link.AppleZoom alignmentRect={{ x: 0, y: 0, width: 200, height: 300 }}>79<Image source={{ uri: "https://example.com/thumb.jpg" }} />80</Link.AppleZoom>81```8283Coordinates 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.8485## Controlling Dismissal8687Zoom 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.8889### Disable all dismissal gestures9091```tsx92import { usePreventZoomTransitionDismissal } from "expo-router";9394export default function PhotoScreen() {95usePreventZoomTransitionDismissal();96return <Image source={{ uri: "https://example.com/full.jpg" }} />;97}98```99100### Restrict dismissal to a specific area101102Use `unstable_dismissalBoundsRect` to prevent conflicts with scrollable content:103104```tsx105usePreventZoomTransitionDismissal({106unstable_dismissalBoundsRect: {107minX: 0,108minY: 0,109maxX: 300,110maxY: 300,111},112});113```114115This is useful when the destination contains a zoomable scroll view — the system gives that scroll view precedence over the dismiss gesture.116117## Combining with Link.Preview118119Zoom transitions work alongside long-press previews:120121```tsx122<Link href="/photo" asChild>123<Link.Trigger withAppleZoom>124<Pressable>125<Image126source={{ uri: "https://example.com/thumb.jpg" }}127style={{ width: 120, height: 120 }}128/>129</Pressable>130</Link.Trigger>131<Link.Preview />132</Link>133```134135## Best Practices136137**Good use cases:**138- Thumbnail → full image (gallery, profile photos)139- Card → detail screen with similar visual content140- Source and destination with similar aspect ratios141142**Avoid:**143- Skinny full-width list rows as zoom sources — the transition looks unnatural144- Mismatched aspect ratios between source and destination without `alignmentRect`145- Using zoom with sheets or popovers — only works in Stack navigator146- Hiding the navigation bar — known issues with header visibility during transitions147148**Tips:**149- Always provide a close or back button — dismissal gestures are not discoverable150- If the destination has a zoomable scroll view, use `unstable_dismissalBoundsRect` to avoid gesture conflicts151- Source view doesn't need to match the tap target — only the `Link.AppleZoom` wrapped element animates152- When source is unavailable (e.g., scrolled off screen), the transition zooms from the center of the screen153154## References155156- Expo Router Zoom Transitions: https://docs.expo.dev/router/advanced/zoom-transition/157- Link.AppleZoom API: https://docs.expo.dev/versions/v55.0.0/sdk/router/#linkapplezoom158- Apple UIKit Fluid Transitions: https://developer.apple.com/documentation/uikit/enhancing-your-app-with-fluid-transitions159