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.

Open the generator ↓
Presets
7
Easings
7
Options
8
Gzipped
5.8 KB
88%start53%endwindow32progress 0top at 88%y 32pxopacity 08progress 0.5ease-out → 0.75y 8pxopacity 0.75progress 1top at 53%y 0pxopacity 1
Fig. 1 — fadeUp, the default: a card starts 32px low and transparent when its top crosses 88% of the window, and is in place by 53%. The dotted box is where it sits in the layout, and the window is measured from there.
01The tool

Make your scroll reveal

preset
once

9 cards · each starts 5.25% of the window after the one before it · corner: eased progress

main.js8 lines
// <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.

02The model

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.

main.js13 lines
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();
OptionDefaultWhat it does
preset'fadeUp'The start state, by name: one of the 7 below.
fromnoneA 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.
stagger0.15How 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.
oncetrueKeep 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.
onEnternoneCalled each time the first element enters its window, scrolling down, once: true included.
onLeavenoneCalled each time the last element passes the end of its window, scrolling down.
Its place
The window is measured from where the element sits in the layout, not from where its start state has moved it, so a card that starts 32px low still starts on time.
JavaScript
scrollReveal always runs on the library’s JavaScript scroll engine, never on CSS scroll timelines, so it behaves the same in every browser.
037 presets

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.

PresetStarts atWhat it does
fadeUpopacity 0 · y 32pxRises 32px into place as it fades in. The default.
fadeDownopacity 0 · y -32pxDrops 32px into place from above.
fadeLeftopacity 0 · x 32pxStarts 32px to the right and slides left into place.
fadeRightopacity 0 · x -32pxStarts 32px to the left and slides right into place.
scaleopacity 0 · scale 0.88Grows from 0.88 of its size to full size.
flipopacity 0 · rotateX 20°Turns 20° around its horizontal axis into place.
flipXopacity 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.

Flips
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.
Its own CSS
The end state stays on the element as inline styles, 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.
04Rows and replays

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.

CardRowPlaceStartsIn place
0110top 88%top 53%
0211top 82.75%top 47.75%
0312top 77.5%top 42.5%
0420top 88%top 53%
0521top 82.75%top 47.75%
0622top 77.5%top 42.5%
Scrolling back
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.
Reduced motion
For readers who ask their system for less motion, every element is shown in its final state at once, with nothing moving, including when the setting is turned on while the page is open.
05Two ways in

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.

WhatscrollRevealAOS drop-in
Set up withscrollReveal('.card')data-aos="fade-up" + AOS.init()
Moves withThe scroll positionA CSS transition, started at the trigger
StaggerBy row, one stagger valuedata-aos-delay on each element
Gzipped5.8 KB3.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.

067 questions

Scroll reveal questions

Q1

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.

Q2

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.

Q3

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.

Q4

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.

Q5

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.

Q6

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.

Q7

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.