Skip to content

@remotion/gsap: Fix delayed fromTo leaving from-values behind on remount - #12180

Merged
JonnyBurger merged 2 commits into
remotion-dev:mainfrom
Fats403:gsap-fromto-remount
Oct 12, 2026
Merged

JonnyBurger merged 2 commits into
remotion-dev:mainfrom
Fats403:gsap-fromto-remount

Conversation

@Fats403

@Fats403 Fats403 commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

useGsapTimeline() primed the timeline inside its gsap.context(). Seeking a fromTo with immediateRender: false makes GSAP create that tween's start state lazily, so the context recorded it. On cleanup, context.revert() undid the start state first, then the owning tween, whose revert puts its from-values back on the element. Nothing removes them after that. The next mount on the same element (Strict Mode, or a rebuild from dependencies) reads them as the element's own style.

What it looked like for us: an end title animated with

timeline.fromTo(
	selector('[data-title]'),
	{yPercent: 60, opacity: 0},
	{yPercent: 0, opacity: 1, duration: 0.25, ease: 'expo.out', immediateRender: false},
	'slam+=0.5',
);

sat 60% lower in the Player, cut off by its overflow: hidden mask (transform: translate(0px, 66px) at the end of the timeline), and the offset compounded with every remount. Exports were fine since nothing remounts there.

The fix primes and seeks outside the context. The context still records everything the builder creates, and each tween reverts its own start state in the right order.

Minimal repro with GSAP 3.15.0 alone, no React:

const ctx = gsap.context(() => {
	const tl = gsap.timeline({paused: true});
	tl.fromTo(el, {yPercent: 60}, {yPercent: 0, duration: 0.25, immediateRender: false}, 0.7);
	tl.totalTime(1.2, true);
});
ctx.revert();
el.style.transform; // "translate(0%, 60%)", expected ""

timeline.revert() restores it correctly, as does a fromTo with the default immediateRender or a from() with immediateRender: false. Happy to report the context ordering to GreenSock too if that's useful.

Testing:

  • Two new tests in packages/gsap: a Strict Mode mount, and a rebuild from dependencies. Both fail on main (opacity 0.2, the from-value, instead of the element's 0.25) and pass with the fix.
  • bun test src/test: 48 pass. tsgo clean, lint unchanged.

…remount

useGsapTimeline() primed the timeline inside its gsap.context(). Seeking
a fromTo with immediateRender: false makes GSAP create the tween's start
state lazily, so the context recorded it. context.revert() then undid
the start state before its owner, and the owner's revert put the
from-values back on the element. The next mount on the same element
(Strict Mode, a dependency rebuild) read them as the element's own style.

Prime outside the context so each tween reverts its own start state.
@vercel

vercel Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
bugs Ready Ready Preview Oct 12, 2026 8:36am UTC
remotion Ready Ready Preview Oct 12, 2026 8:36am UTC

Request Review

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • packages/gsap/src/use-gsap-timeline.ts — Closes the context.add() callback after state.controls.pause() and runs the initial playhead sweep outside the context. The _startAt tween that GSAP lazily creates for a fromTo/from with immediateRender: false is no longer captured by context.revert(), so it is no longer reverted ahead of its owning tween. The owner reverts its own start state, which restores the element's React style rather than leaving the from-values behind on the element for the next mount.
  • packages/gsap/src/test/use-gsap-timeline.test.tsx — Adds two regression tests (Strict Mode setup-cleanup-setup, and a rebuild from dependencies) asserting the element's own opacity: 0.25 survives a delayed fromTo. I confirmed both fail against 32af7e8f and pass with this change.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash | 𝕏

@JonnyBurger
JonnyBurger merged commit b645cff into remotion-dev:main Oct 12, 2026
18 checks passed

This branch was successfully deployed

2 active deployments
Preview – remotion — f4665a8a Deployed Oct 12, 2026 by vercel[bot]
Preview – bugs — f4665a8a Deployed Oct 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants