Problem
In motion-dom, the animateTarget function in animation/interfaces/visual-element-target.mjs accesses window.MotionHandoffAnimation (line 62) without a typeof window guard:
// visual-element-target.mjs:62
if (window.MotionHandoffAnimation) {
When window is undefined (e.g., in non-browser JS runtimes, or when shadowed by an IIFE wrapper), this throws:
TypeError: Cannot read properties of undefined (reading 'MotionHandoffAnimation')
Other modules in the same codebase correctly guard against this. For example, render/utils/reduced-motion/index.mjs:
const isBrowser = typeof window !== "undefined";
Affected files
The following files in motion-dom access window without a typeof guard (all inside function bodies, not module-level):
animation/interfaces/visual-element-target.mjs — window.MotionHandoffAnimation (the immediate crash)
render/VisualElement.mjs — window.MotionCheckAppearSync
projection/node/HTMLProjectionNode.mjs — window, window.getComputedStyle
projection/node/create-projection-node.mjs — window.MotionHasOptimisedAnimation, window.MotionCancelOptimisedAnimation, window.innerWidth
render/dom/style-computed.mjs — window.getComputedStyle
render/html/HTMLVisualElement.mjs — window.getComputedStyle
animation/keyframes/DOMKeyframesResolver.mjs — window.pageYOffset, window.getComputedStyle
animation/keyframes/KeyframesResolver.mjs — window.scrollTo
animation/utils/css-variables-conversion.mjs — window.getComputedStyle
gestures/hover.mjs — window.addEventListener/removeEventListener
gestures/press/index.mjs — window.addEventListener/removeEventListener
resize/handle-window.mjs — window.innerWidth/innerHeight, window.addEventListener
utils/supports/scroll-timeline.mjs — window.ScrollTimeline
Context
This issue was discovered while using framer-motion/dom in Lynx, a cross-platform UI framework. Lynx's web runtime wraps main-thread scripts in an IIFE that shadows window to simulate a non-browser environment:
(function(){ "use strict"; const window = void 0; /* bundled code */ })()
The const window = void 0 creates an immutable lexical binding that shadows the global window. All bare window accesses inside this IIFE see undefined instead of the real window object.
typeof window is safe because typeof never throws, even on undefined values. The reduced-motion module already uses this pattern correctly.
Suggested fix
Add a typeof window !== "undefined" guard before accessing window properties. For example in visual-element-target.mjs:
// Before:
if (window.MotionHandoffAnimation) {
// After:
if (typeof window !== "undefined" && window.MotionHandoffAnimation) {
This matches the existing pattern used in reduced-motion/index.mjs and is a standard practice for code that may run outside a browser context.
Problem
In
motion-dom, theanimateTargetfunction inanimation/interfaces/visual-element-target.mjsaccesseswindow.MotionHandoffAnimation(line 62) without atypeof windowguard:When
windowisundefined(e.g., in non-browser JS runtimes, or when shadowed by an IIFE wrapper), this throws:Other modules in the same codebase correctly guard against this. For example,
render/utils/reduced-motion/index.mjs:Affected files
The following files in
motion-domaccesswindowwithout atypeofguard (all inside function bodies, not module-level):animation/interfaces/visual-element-target.mjs—window.MotionHandoffAnimation(the immediate crash)render/VisualElement.mjs—window.MotionCheckAppearSyncprojection/node/HTMLProjectionNode.mjs—window,window.getComputedStyleprojection/node/create-projection-node.mjs—window.MotionHasOptimisedAnimation,window.MotionCancelOptimisedAnimation,window.innerWidthrender/dom/style-computed.mjs—window.getComputedStylerender/html/HTMLVisualElement.mjs—window.getComputedStyleanimation/keyframes/DOMKeyframesResolver.mjs—window.pageYOffset,window.getComputedStyleanimation/keyframes/KeyframesResolver.mjs—window.scrollToanimation/utils/css-variables-conversion.mjs—window.getComputedStylegestures/hover.mjs—window.addEventListener/removeEventListenergestures/press/index.mjs—window.addEventListener/removeEventListenerresize/handle-window.mjs—window.innerWidth/innerHeight,window.addEventListenerutils/supports/scroll-timeline.mjs—window.ScrollTimelineContext
This issue was discovered while using
framer-motion/domin Lynx, a cross-platform UI framework. Lynx's web runtime wraps main-thread scripts in an IIFE that shadowswindowto simulate a non-browser environment:The
const window = void 0creates an immutable lexical binding that shadows the globalwindow. All barewindowaccesses inside this IIFE seeundefinedinstead of the real window object.typeof windowis safe becausetypeofnever throws, even onundefinedvalues. Thereduced-motionmodule already uses this pattern correctly.Suggested fix
Add a
typeof window !== "undefined"guard before accessingwindowproperties. For example invisual-element-target.mjs:This matches the existing pattern used in
reduced-motion/index.mjsand is a standard practice for code that may run outside a browser context.