plans/issues/issue-2260.md
ref.current too early — and closeExecutor instructions: Follow this plan step by step. If anything in "STOP conditions" occurs, stop and report. When done, update this plan's row in
plans/issues/README.md.Drift check (run first):
gh api repos/motiondivision/motion/issues/2260 --jq '.state'→open(if closed, mark DONE and stop).
42bfbe3ed, 2026-06-11The reporter (0 comments, 2023) believes sequences only accept selectors and
motion values. They accept Elements — and therefore refs — but the
reporter's snippet evaluates ref.current during render, before React
attaches refs, so the segments capture null and are silently skipped. The
fix is a one-line usage change; closing with the explanation resolves it.
DOMSegment subjects are typed ElementOrSelector
(packages/framer-motion/src/animation/sequence/types.ts:76-82), and
ElementOrSelector includes Element | Element[] | NodeListOf<Element> | string | null | undefined
(packages/motion-dom/src/utils/resolve-elements.ts:1-7).packages/framer-motion/src/animation/animate/resolve-subjects.ts:24-36 —
runtime resolution: null/undefined subjects return [] (silent
skip — this is why the reporter saw "only the motion value animates"
rather than an error); strings go through resolveElements; a bare
Element falls to the final branch and returns [subject]. Sequences use
this via sequence/create.ts:334-339.const introSeq: AnimationSequence = [
[intro.serviceRef.current, { opacity: 0 }, ...], // .current === null at render
[intro.progress, 1, ...], // motion value — works
...
]
useEffect(() => { animate(introSeq) }, [])
ref.current is null when the array literal is evaluated; by the time
useEffect runs, the array still holds the captured nulls. Moving the
array construction inside useEffect (or using useAnimate()'s scope +
selectors) makes every segment animate.Confirm sequences animate plain Elements:
grep -rn "document.createElement\|createRef" packages/framer-motion/src/animation/animate/__tests__/animate-sequence.test.ts packages/framer-motion/src/animation/sequence/__tests__/index.test.ts 2>/dev/null | head
— existing tests construct sequences with raw Elements (the sequence suite
runs on document.createElement fixtures). Run
npx jest --config packages/framer-motion/jest.config.json --testPathPattern="sequence"
→ pass. If no Element-subject test exists, run a throwaway check (do not
commit).
plans/issues/README.md row APPROVED): Answer and closeComment on #2260: refs are supported — the subject type is
Element | selector | MotionValue; the snippet fails because ref.current
is read at render time; show the corrected pattern (build the sequence inside
useEffect). Mention that null subjects are skipped silently by design
(resolve-subjects.ts), which is why nothing errored. Close:
gh api -X PATCH repos/motiondivision/motion/issues/2260 -f state=closed -f state_reason=completed
plans/issues/README.md row updatedElement subject does NOT animate in a sequence →
premise wrong; report with the failing case.null would have
made this self-diagnosing. Deliberately NOT planned here (silent-skip is
also a feature for conditional targets); note it if the maintainer wants a
follow-up.