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

  1. Wrap the <video>/<iframe> in <Activity mode="offscreen"> in the optimistic state so it is hidden rather than duplicated.
  2. Remove the element from the optimistic tree if it does not need to be visible during the gesture.
  3. If duplication is acceptable, ignore the one-time warning but verify playback/scroll position still behaves after the transition.
  4. 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

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


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/66e787ea6b1da833. Report an issue: GitHub.