MatchedGeometryEffect: Shared Element Hero Transitions
MatchedGeometryEffect lets two views share one spatial identity, so a thumbnail can appear to fly into a detail sheet across view hierarchies. It only works if both views exist simultaneously during a withAnimation block; otherwise the transition snaps.
WHY IT EXISTS: Before matched geometry effects existed, building a hero animation in SwiftUI required manually reading global frames with GeometryReader, converting between coordinate spaces, and driving animation values yourself. This was fragile because view hierarchies often changed during transitions, causing frame reads to return incorrect or zero rectangles. MatchedGeometryEffect was introduced to automate the spatial bridging between two views so the framework could handle the interpolation reliably.
THE MENTAL MODEL: Think of it as giving two separate views the same seat in the room. Even though they live in different parts of the view hierarchy and may never exist on screen at the same time for long, SwiftUI treats them as the same object moving through space. One view donates its current frame as the starting rectangle, and the other view receives that rectangle as its target. The system then animates a proxy representation between those two rectangles while the actual views swap in and out.
HOW IT WORKS: You create a Namespace.ID, typically stored as a state property using Namespace. You attach the matchedGeometryEffect modifier to two views, giving each the same string identifier and the same namespace instance. One view acts as the source and the other as the destination. When a state change triggers a transition, SwiftUI captures the global bounds of both views and interpolates the position and size during the animation window. The modifier only affects frame geometry; it does not automatically animate opacity, corner radius, or other visual properties unless you add those animations separately.
WHEN TO USE IT: Use this modifier when you want a shared element transition, such as a thumbnail flying into a detail image, a list row expanding into a card, or a sidebar icon morphing into a toolbar button. It shines whenever the same conceptual content appears in two visually distinct places and you want to reinforce the relationship with a spatial animation rather than a simple fade.
WHEN NOT TO USE IT: Do not use it for views that already live in the same container and can be repositioned with a simple frame or offset change. Avoid it when you need to animate non-geometric properties like color or shadow radius, because the modifier ignores them. It is also a poor fit for drag-driven interactive transitions where the user's finger position must drive the animation progress in real time, since matched geometry is designed for state-driven transitions rather than gesture-driven scrubbing.
ONE CANONICAL EXAMPLE: Imagine a photo grid where tapping a thumbnail opens a full-screen detail view. Both the grid cell and the detail image carry matchedGeometryEffect with identical identifiers and a shared namespace. Wrapped inside a withAnimation block, the state change that swaps the grid for the detail triggers SwiftUI to project an animated proxy that appears to carry the photo from its small grid position to the large centered frame, creating a smooth hero transition without manual frame calculations.
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.