tool / scroll revealjavascript · react
Scroll reveal generator
Pick a preset, set the stagger, the easing and the trigger window, and watch cards reveal as a box scrolls. Then copy the code: one scrollReveal call in JavaScript, or a React component.
- Presets
- 7
- Easings
- 7
- Options
- 8
- Gzipped
- 5.8 KB
Make your scroll reveal
9 cards · each starts 5.25% of the window after the one before it · corner: eased progress
// <div class="cards">
// <article class="card">…</article>
// <article class="card">…</article>
// </div>
import { scrollReveal } from 'svg-scroll-draw/reveal';
scrollReveal('.card');On real scroll · scrollReveal itself, the same options, no box
These follow the page: each card starts when its top reaches 88% of the window and is in place at 53%, the next in its row a little later.
How a reveal follows the scroll
Each element gets its own progress, a number from 0 to 1 read from the scroll: 0 when its top reaches 88% of the window, 1 when it reaches 53%, a little later for the next ones in a row (section 4). The easing shapes that number, and the element’s style follows it from the start state to where it sits (fig. 1). So there is no duration: scroll faster and it plays faster, stop half way and it waits half way.
import { scrollReveal } from 'svg-scroll-draw/reveal';
// Every default written out: the same as scrollReveal('.card').
const reveal = scrollReveal('.card', {
preset: 'fadeUp', // opacity 0 · y 32px
stagger: 0.15,
easing: 'ease-out',
once: true,
trigger: { start: 'top 88%', end: 'top 53%' },
});
// When the page changes: every element's inline styles back as they were.
reveal.destroy();| Option | Default | What it does |
|---|---|---|
| preset | 'fadeUp' | The start state, by name: one of the 7 below. |
| from | none | A start state of your own: opacity, x and y in px, scale, rotate, rotateX and rotateY in degrees. Each key wins over the preset’s, and each animates to its resting value: opacity and scale to 1, the rest to 0. |
| stagger | 0.15 | How far behind its neighbour in the same row each element starts, as a fraction of the reveal window, from 0 to 1. |
| easing | 'ease-out' | 'ease-out', 'ease-in-out', 'ease-in', 'linear', 'spring', 'bounce', 'elastic', or any function of t. |
| once | true | Keep each element at the furthest it has got, instead of reversing as you scroll back up. In 2.23.2 and earlier it kept the highest eased value, so spring and elastic stopped past their place and bounce lost its dips; fixed in 2.23.3, which keeps the scroll progress and eases it after. |
| trigger | { start: 'top 88%', end: 'top 53%' } | The element’s edge and a line down the window: it starts when its top is 88% down the window and is in place at 53%. Either key can be set alone. |
| onEnter | none | Called each time the first element enters its window, scrolling down, once: true included. |
| onLeave | none | Called each time the last element passes the end of its window, scrolling down. |
Every preset, or your own
Each preset is a start state; every element animates from it to opacity 1, scale 1, no offset and no turn, written on the element as inline styles.
| Preset | Starts at | What it does |
|---|---|---|
| fadeUp | opacity 0 · y 32px | Rises 32px into place as it fades in. The default. |
| fadeDown | opacity 0 · y -32px | Drops 32px into place from above. |
| fadeLeft | opacity 0 · x 32px | Starts 32px to the right and slides left into place. |
| fadeRight | opacity 0 · x -32px | Starts 32px to the left and slides right into place. |
| scale | opacity 0 · scale 0.88 | Grows from 0.88 of its size to full size. |
| flip | opacity 0 · rotateX 20° | Turns 20° around its horizontal axis into place. |
| flipX | opacity 0 · rotateY 20° | Turns 20° around its vertical axis into place. |
from changes a preset key by key: preset: 'fadeLeft', from: { x: 80 } keeps the fade and slides 80px instead of 32. Given alone, from is laid over the default, fadeUp, so from: { y: 0 } is a plain fade.
flip and flipX turn the element with rotateX and rotateY and bring no perspective of their own, so on a flat parent the turn reads as a slight squash. Give the parent a perspective, such as perspective: 800px, for depth; the generator adds it for these two.opacity: 1 and a transform such as translateY(0px), and inline styles win over a stylesheet: an opacity or a transform the element gets from your CSS, a hover lift for example, does not show while scrollReveal runs. Put those on a child of the revealed element. destroy() puts the element’s own inline styles back.Stagger by row, once, and reduced motion
stagger starts each element a fraction of its window later than the one before it in its row. The default window runs from 88% to 53%, 35% of the window’s height, so the default 0.15 puts each card 5.25% after its neighbour. Rows come from the layout: elements whose tops line up count 0, 1, 2…, and one further down starts a new row at 0, since its place on the page already delays it. However long the row, none starts more than one whole window after its first.
| Card | Row | Place | Starts | In place |
|---|---|---|---|---|
| 01 | 1 | 0 | top 88% | top 53% |
| 02 | 1 | 1 | top 82.75% | top 47.75% |
| 03 | 1 | 2 | top 77.5% | top 42.5% |
| 04 | 2 | 0 | top 88% | top 53% |
| 05 | 2 | 1 | top 82.75% | top 47.75% |
| 06 | 2 | 2 | top 77.5% | top 42.5% |
once: true, the default, keeps each element at the furthest it has got: scroll back up and it stays. With once: false it follows the scroll both ways, playing backwards as you scroll up and hiding again below the start line. In 2.23.2 and earlier, once: true kept the highest eased value, so with spring or elastic each element stopped past its place, on the top of the overshoot, and bounce lost its dips; fixed in 2.23.3, which keeps the furthest scroll progress and eases it after.scrollReveal or the AOS drop-in
svg-scroll-draw reveals on scroll in two ways. scrollReveal is a JavaScript call whose motion follows the scroll. The AOS drop-in, svg-scroll-draw/aos, speaks AOS’s data-aos markup and AOS.init(): once an element crosses its trigger it plays a CSS transition of a set duration, as AOS 2.3.4 does. Keep an AOS site’s HTML as it is with the drop-in; write new code with either.
| What | scrollReveal | AOS drop-in |
|---|---|---|
| Set up with | scrollReveal('.card') | data-aos="fade-up" + AOS.init() |
| Moves with | The scroll position | A CSS transition, started at the trigger |
| Stagger | By row, one stagger value | data-aos-delay on each element |
| Gzipped | 5.8 KB | 3.2 KB, styles included |
Every data-aos effect, live: AOS animation examples. Sizes, features and the same reveal written for each: svg-scroll-draw vs AOS.
Scroll reveal questions
How do I make a scroll reveal animation?
Call scrollReveal on the elements: scrollReveal('.card'), imported from svg-scroll-draw/reveal. By default each one fades in and rises 32px as its top moves from 88% to 53% of the window, and the cards in a row follow one another a little apart. There is no stylesheet to add. The generator above sets the options and writes the call.
How do I reveal elements on scroll in React?
Call scrollReveal in a useEffect on the elements a ref holds, and return its destroy() as the cleanup: const r = scrollReveal(ref.current.querySelectorAll('.card')); return () => r.destroy(). destroy() puts every element’s inline styles back as they were, so Strict Mode, which runs effects twice in development, leaves nothing behind. In the Next.js App Router the component needs 'use client'. The React tab of the generator writes the whole component.
How do I animate on scroll with plain JavaScript?
Import scrollReveal from svg-scroll-draw/reveal and call it with a selector, a NodeList or an array of elements. It is one function with typed options instead of attributes in your HTML, and it is 5.8 KB gzipped with nothing else to load. To keep AOS’s data-aos markup instead, the AOS drop-in, svg-scroll-draw/aos, runs it as it is.
How do I make a reveal play only once, or reverse when scrolling up?
once: true, the default, keeps every element at the furthest it has got, so scrolling back up leaves it where it is. once: false ties it to the scroll both ways: scrolling up plays it backwards, and it hides again once its top drops below the start line. In 2.23.2 and earlier, once: true with a spring or elastic easing stopped each element past its place, on the top of the overshoot; 2.23.3 fixes that, and it settles in place.
Why does scrollReveal not animate inside a scrolling div?
scrollReveal follows the window’s scroll. It has no scrollContainer option, so elements in a panel that scrolls on its own do not move as the panel scrolls. For that case, scrollAnimate from svg-scroll-draw takes a scrollContainer option and explicit props such as opacity: [0, 1]; the preview box above is built that way.
Does scrollReveal respect prefers-reduced-motion?
Yes. For readers who ask their system for less motion, every element is shown in its final state at once, with nothing moving. A reader who turns the setting on while the page is open gets the same from that moment.
Is scrollReveal the same as ScrollReveal.js?
No. ScrollReveal.js is a separate library (4.0.9), licensed GPL-3.0. scrollReveal is svg-scroll-draw’s own function, with the options on this page. svg-scroll-draw also has an MIT drop-in for ScrollReveal.js’s API, svg-scroll-draw/scrollreveal, so a site using it can switch by changing the import.
The generator is free, and the code it writes uses svg-scroll-draw 2.23.3, MIT-licensed. Words instead of cards: the text animation generator. All of it: the docs.