facebook/react · warning
startGestureTransition() required cloning a <%s> element sin
Error message
startGestureTransition() required cloning a <%s> element since it exists in both states of the gesture. This can be problematic since it will load it twice Try removing or hiding it with <Activity mode="offscreen"> in the optimistic state.
What it means
During a gesture-driven View Transition (startGestureTransition / <ViewTransition> optimistic updates), react-dom must keep both the current and optimistic state mounted; when a DOM node exists in both states it is cloned via cloneInstance (packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js:682). Cloning <video> or <iframe> is expensive and semantically wrong — the media loads twice and playback state diverges — so react-dom warns once per session (didWarnForClone) telling you to remove the element from one state or hide it with <Activity mode="offscreen">. The clone still happens; this is a performance/correctness warning, not a failure.
Source
Thrown at packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js:682
}
let didWarnForClone = false;
export function cloneMutableInstance(
instance: Instance,
keepChildren: boolean,
): Instance {
if (__DEV__) {
// Warn for problematic
const tagName = instance.tagName;
switch (tagName) {
case 'VIDEO':
case 'IFRAME':
if (!didWarnForClone) {
didWarnForClone = true;
// TODO: Once we have the ability to avoid cloning the root, suggest an absolutely
// positioned ViewTransition instead as the solution.
console.warn(
'startGestureTransition() required cloning a <%s> element since it exists in ' +
'both states of the gesture. This can be problematic since it will load it twice ' +
'Try removing or hiding it with <Activity mode="offscreen"> in the optimistic state.',
tagName.toLowerCase(),
);
}
break;
}
}
return instance.cloneNode(keepChildren);
}
export function appendInitialChild(
parentInstance: Instance,
child: Instance | TextInstance,
): void {
// Note: This should not use moveBefore() because initial are appended while disconnected.
parentInstance.appendChild(child);View on GitHub (pinned to eafeac097b)
Solutions
- Wrap the <video>/<iframe> in <Activity mode="offscreen"> in the optimistic state so it is hidden rather than duplicated.
- Remove the element from the optimistic tree if it does not need to be visible during the gesture.
- If duplication is acceptable, ignore the one-time warning but verify playback/scroll position still behaves after the transition.
- As an alternative, key the media element differently across states and animate it with an absolutely positioned ViewTransition instead of relying on cloning.
Example fix
// before
function OptimisticState() {
return <video src="/movie.mp4" controls />;
}
// after
function OptimisticState() {
return (
<Activity mode="offscreen">
<video src="/movie.mp4" controls />
</Activity>
);
} Defensive patterns
Strategy: fallback
Prevention
- Wrap persistent <video>/<iframe> elements in <Activity mode="offscreen"> in the optimistic gesture state.
- Keep media elements out of one of the two gesture states when they do not need to be visible.
- Remember the warning fires only once per session (didWarnForClone), so audit all media elements up front.
When it happens
Trigger: A gesture transition (e.g. useSwipeTransition / startGestureTransition optimistic state) where the same <video> or <iframe> persists in both the pre-gesture and optimistic trees without an Activity boundary, forcing react-dom to clone it so both states render simultaneously.
Common situations: Gesture-driven page transitions (swipe navigation) over media players or embedded maps/videos; optimistic UI that keeps the same keyed media element across states; enabling View Transitions on routes that contain iframes.
Related errors
- Cannot cancel root view transition on a comment node. All vi
- Not implemented.
- startGestureTransition is not yet supported in react-art.
- 62
- A React form was unexpectedly submitted. If you called form.
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/66e787ea6b1da833.
Report an issue: GitHub.