@@ -661,12 +661,21 @@ function commitAppearingPairViewTransitions(placement: Fiber): void {
661661 // We found a new appearing view transition with the same name as this deletion.
662662 // We'll transition between them.
663663 viewTransitionHostInstanceIdx = 0 ;
664- applyViewTransitionToHostInstances (
664+ const inViewport = applyViewTransitionToHostInstances (
665665 child . child ,
666666 props . name ,
667667 null ,
668668 false ,
669669 ) ;
670+ if ( ! inViewport ) {
671+ // This boundary is exiting within the viewport but is going to leave the viewport.
672+ // Instead, we treat this as an exit of the previous entry by reverting the new name.
673+ // Ideally we could undo the old transition but it's now too late. It's also on its
674+ // on snapshot. We have know was for it to paint onto the original group.
675+ // TODO: This will lead to things unexpectedly having exit animations that normally
676+ // wouldn't happen. Consider if we should just let this fly off the screen instead.
677+ restoreViewTransitionOnHostInstances ( child . child , false ) ;
678+ }
670679 }
671680 }
672681 commitAppearingPairViewTransitions ( child ) ;
@@ -733,13 +742,25 @@ function commitDeletedPairViewTransitions(
733742 if ( name != null && name !== 'auto' ) {
734743 const pair = appearingViewTransitions . get ( name ) ;
735744 if ( pair !== undefined ) {
736- const oldinstance : ViewTransitionInstance = child . stateNode ;
737- const newInstance : ViewTransitionInstance = pair ;
738- newInstance . paired = oldinstance ;
739745 // We found a new appearing view transition with the same name as this deletion.
740- // We'll transition between them.
741746 viewTransitionHostInstanceIdx = 0 ;
742- applyViewTransitionToHostInstances ( child . child , name , null , false ) ;
747+ const inViewport = applyViewTransitionToHostInstances (
748+ child . child ,
749+ name ,
750+ null ,
751+ false ,
752+ ) ;
753+ if ( ! inViewport ) {
754+ // This boundary is not in the viewport so we won't treat it as a matched pair.
755+ // Revert the transition names. This avoids it flying onto the screen which can
756+ // be disruptive and doesn't really preserve any continuity anyway.
757+ restoreViewTransitionOnHostInstances ( child . child , false ) ;
758+ } else {
759+ // We'll transition between them.
760+ const oldinstance : ViewTransitionInstance = child . stateNode ;
761+ const newInstance : ViewTransitionInstance = pair ;
762+ newInstance . paired = oldinstance ;
763+ }
743764 // Delete the entry so that we know when we've found all of them
744765 // and can stop searching (size reaches zero).
745766 appearingViewTransitions . delete ( name ) ;
0 commit comments