Loading and layout readiness
A transition needs mounted, measurable endpoints. ChoreographyScreen waits for its layout before declaring readiness. Use its ready prop when the destination's layout also depends on data, images, or fonts.
Prefer a screen-level gate
function DetailScreen() {
const { data, isLoading } = useArtwork(); // Your application data hook.
return (
<ChoreographyScreen screenId="Detail" ready={!isLoading && !!data}>
{data ? <DetailContent artwork={data} /> : <LoadingState />}
</ChoreographyScreen>
);
}ready defaults to true. Set it to false to hold preparation, then back to true when your content is ready. Layout checks still apply. Keep the wrapper mounted while readiness changes.
Give empty targets explicit dimensions or a layout that resolves to nonzero bounds. An empty SharedElement.Target has no intrinsic content size before the owner arrives.
Let a child hold readiness
useChoreographyBlocker() gives a child component an acquire() function. Each call holds readiness and returns a release function that is safe to call more than once. Release every blocker before the screen can become ready.
import { useLayoutEffect, type ReactNode } from 'react';
import { useChoreographyBlocker } from 'react-native-screen-choreography';
function LayoutGate({
ready,
children,
}: {
ready: boolean;
children: ReactNode;
}) {
const { acquire } = useChoreographyBlocker();
useLayoutEffect(() => {
if (ready) return;
const release = acquire();
return release; // Releases when ready changes or the child unmounts.
}, [acquire, ready]);
return children;
}Render this beneath ChoreographyScreen so it blocks the correct screen. Use a stable ready value that becomes true for both success and a usable failure state. A blocker is independent of the screen's ready prop: both gates must open.
Delay expensive supporting content
The shared target geometry needs to be ready. A long description or expensive secondary section often does not. Use useLatchedReveal to delay mounting it until progress reaches a threshold. A declarative Enter role animates already mounted content; it does not defer the content's rendering or layout work.
import { useLatchedReveal } from 'react-native-screen-choreography';
function SupportingContent({ artworkId }: { artworkId: string }) {
const visible = useLatchedReveal({
startProgress: 0.7,
resetKey: artworkId,
});
return visible ? <ArtworkDetails id={artworkId} /> : null;
}Place this component beneath ChoreographyScreen. Supporting content stays unmounted while the destination gets ready, then mounts once when the transition reaches the threshold. Once visible, it stays mounted through reverse motion and later transitions.
Set resetKey to the content's identity, as with artworkId above, so a reused screen starts a fresh gate when it shows different content. The default visibleWhenInactive: true keeps content visible when the screen is opened without a shared transition.
useLatchedReveal returns a boolean; it does not animate opacity. Use it for companion content, never to conditionally mount a target the transition needs to measure. Keep placeholder dimensions stable when omitted content affects shared target geometry. A threshold of 0.7 still mounts the section during the animation, so very expensive content may need further splitting or application-level deferral until the transition settles.
Finish motion when the user interacts
Use settleTransition when ordinary screen interaction should finish the current motion in favor of that screen:
const { settleTransition } = useChoreographyControls();
<ScrollView onScrollBeginDrag={settleTransition}>
<DetailSections />
</ScrollView>;Import useChoreographyControls from your integration entry and call it in a component beneath ChoreographyScreen. Use useChoreographyProgress instead when you also need the animation clock or backdrop style.
See the hook reference for reveal defaults and troubleshooting for measurement failures.