Scroll

toggleActions

toggleActions is a ScrollTrigger string that controls what a linked animation does at four scroll moments: onEnter, onLeave, onEnterBack, onLeaveBack. You write four space-separated keywords in that exact order, and each one tells the tween or timeline to play, reverse, pause, resume, restart, reset, complete, or do nothing.

Updated June 30, 2026

Mechanics

How toggleActions works

A ScrollTrigger crosses its start and end lines as the user scrolls. Those crossings produce four directional events, and toggleActions maps one action to each, in this fixed order: onEnter onLeave onEnterBack onLeaveBack.

  • onEnter fires when you scroll DOWN past start (the trigger enters from below the viewport)
  • onLeave fires when you scroll DOWN past end (the trigger exits out the top)
  • onEnterBack fires when you scroll UP past end (the trigger re-enters from the top)
  • onLeaveBack fires when you scroll UP past start (the trigger exits out the bottom)
reveal.js

Each slot accepts one of: play, pause, resume, reverse, restart, reset, complete, or none. The default is "play none none none", which plays the animation once on enter and ignores every other crossing. reset jumps the animation back to its starting values; reverse plays it backwards from wherever it currently is.

toggleActions and scrub are mutually exclusive

toggleActions fires discrete play/reverse actions at the boundaries. scrub instead binds the animation's playhead to the scrollbar so it has no independent timeline to toggle. If you set both on one ScrollTrigger, scrub wins and toggleActions is ignored. Pick one per trigger.
When

Common toggleActions strings

  • "play none none none" (default) - play once on enter and never undo it. The right pick for headline and hero reveals you don't want re-animating
  • "play none none reverse" - play on enter, reverse when the user scrolls back up past the start. Content tucks itself away again as you leave
  • "play reverse play reverse" - the animation tracks visibility, playing whenever the element is in range and reversing whenever it leaves, in either direction
  • "restart pause resume pause" - restart from the top on every fresh enter, freeze when it leaves, pick up where it paused on the way back
  • "play complete reverse reset" - finish instantly if scrolled past quickly, reverse on the way back up, snap to start once fully below
Alternatives

Use something else when

  • You only ever want the animation to fire once and never reverse - use once: true instead. It plays on enter, then kills the ScrollTrigger entirely so it can't re-fire (the animation itself keeps its end state)
  • The animation should follow the scrollbar 1:1 (parallax, scroll-scrubbed reveals, progress counters) - use scrub instead of toggleActions, they don't combine
  • You need different logic than the eight keywords allow - drop toggleActions and write onEnter / onLeave / onEnterBack / onLeaveBack callback functions, each receives the ScrollTrigger instance
  • You're coordinating many elements entering together with a stagger - ScrollTrigger.batch() collects them and fires one batched callback rather than per-element toggleActions

Reverse-on-leave re-triggers motion

A string like "play reverse play reverse" replays the animation every time the element crosses a boundary, so a user scrolling up and down sees it fire repeatedly. prefers-reduced-motion adoption passed 50% of mobile sites in 2024 (Web Almanac 2024 Accessibility chapter), up from 34% in 2022. Gate the reveal through gsap.matchMedia() and skip the toggle, or fall back to a static end state, for anyone who asked for less motion.
In production

Used in these Annnimate components

Most Annnimate scroll components reach for one of two toggleActions strings, depending on whether the effect should undo itself when it leaves the viewport.

  • Element Reveal uses toggleActions: "play none none reverse" so content slides in on enter and slides back out when you scroll above it, which keeps a long page feeling alive on the way back up
  • Image Dissolve Scroll does NOT use toggleActions at all - its dissolve is bound to the scrollbar with scrub, the canonical case where the two are mutually exclusive and scrub is the right tool
Used in components

See it running in production

FAQ

Common questions

What's the order of the four toggleActions slots?
onEnter, onLeave, onEnterBack, onLeaveBack, always in that order. onEnter and onLeave fire while scrolling down (past start, then past end); onEnterBack and onLeaveBack fire while scrolling back up (past end, then past start). The default is "play none none none".
What's the difference between toggleActions and scrub?
toggleActions fires discrete actions (play, reverse, etc.) when the trigger crosses its start/end lines, so the animation runs on its own timeline at its own speed. scrub binds the animation's playhead directly to scroll position, so it advances and rewinds exactly as you scroll. They can't be combined on one ScrollTrigger - if both are set, scrub wins.
How do I make a ScrollTrigger play once and never reverse?
Either leave toggleActions at its default "play none none none", or pass once: true. once: true is stronger - it kills the ScrollTrigger after the animation finishes the first time, so it can never re-fire even if you later add reverse logic. Use it for hero text and entrance reveals you don't want re-animating.
What does reset do versus reverse in toggleActions?
reverse plays the animation backwards from its current position, so the user watches it undo. reset jumps the animation straight back to its starting values with no in-between, instantly. Use reverse when you want the leave to look animated, reset when you just want the element parked at the start for the next enter.
Why does my toggleActions animation only play the first time?
Your string probably only has an action in the onEnter slot, like "play none none none". Nothing fires on leave or re-enter, so after the first play it sits at its end state. If you want it to replay or reverse, fill the other slots, for example "play none none reverse" or "restart pause resume pause".