---
title: 'Scroll Triggers'
excerpt: 'start/end, toggleActions, and section reveal inside a nested scroller.'
icon: 'ScrollText'
---

## How ScrollTrigger works

**ScrollTrigger** ties a tween (or timeline) to scroll position. You name a `trigger` element, when it should start/end relative to the viewport (`start` / `end`), and either:

- **`toggleActions`** — play / reverse like a gate when thresholds cross (discrete entrances)
- **`scrub`** — map scroll progress directly to tween progress (linked bars, parallax)

In these demos the scroller is the **preview panel** itself (`scroller: stageRef`), not the page — so scroll inside the stage.

## Section reveal

Each section fades and rises when its top hits `85%` of the stage. `toggleActions: 'play none none reverse'` plays on enter and reverses on scroll back — no scrub, so the entrance feels like a normal tween.

Scroll inside the preview: sections animate in as they approach, and reverse if you scroll back up.

<ComponentPreview flush previewClassName="min-h-[280px]">
  <GsapScrollReveal />

  <DocsFileTabs title="Scroll reveal">

```tsx title="scroll-reveal.tsx"
gsap.from(el, {
  y: 40,
  autoAlpha: 0,
  duration: 0.6,
  ease: 'power2.out',
  scrollTrigger: {
    trigger: el,
    scroller: stageRef.current,
    start: 'top 85%',
    toggleActions: 'play none none reverse',
  },
});
```

```html title="scroll-reveal-markup.html"
<div class="space-y-24">
  <section class="gsap-reveal rounded-lg border bg-card p-4">Section 1 — scroll to reveal</section>
  <section class="gsap-reveal rounded-lg border bg-card p-4">Section 2 — scroll to reveal</section>
  <section class="gsap-reveal rounded-lg border bg-card p-4">Section 3 — scroll to reveal</section>
</div>
```

  </DocsFileTabs>
</ComponentPreview>

## Scrubbed progress bar

With `scrub: true`, tween progress **is** scroll progress. `ease: 'none'` keeps the mapping 1:1 — an eased scrub would feel sticky. The bar’s `scaleX` grows from the left as you move through the stage.

Use scrub for continuous indicators. Use toggleActions for one-shot reveals — not both on the same tween.

<ComponentPreview flush previewClassName="min-h-[280px]">
  <GsapScrollScrub />

  <DocsFileTabs title="Scroll scrub">

```tsx title="scroll-scrub.tsx"
gsap.to('.bar', {
  scaleX: 1,
  ease: 'none',
  scrollTrigger: {
    trigger: stageRef.current,
    scroller: stageRef.current,
    start: 'top top',
    end: 'bottom bottom',
    scrub: true,
  },
});
```

```html title="scroll-scrub-markup.html"
<div class="sticky top-0 h-1 rounded-full bg-muted">
  <div class="gsap-scrub-bar h-full w-full origin-left scale-x-0 bg-accent"></div>
</div>
<!-- scroll progress drives scaleX on .gsap-scrub-bar -->
```

  </DocsFileTabs>
</ComponentPreview>
