Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Aduwoayooluwa/animations/llms.txt

Use this file to discover all available pages before exploring further.

Live demo

Scroll animations trigger or respond to the user’s scroll position, creating dynamic effects that enhance storytelling and user engagement. They range from simple fade-in-on-scroll to complex parallax and scroll-driven narratives.
Scroll animations should enhance content, not obstruct it. Always ensure content remains accessible even if animations fail.

Complete code example

import { motion, useScroll, useSpring } from 'framer-motion'

export default function ScrollProgress() {
  const { scrollYProgress } = useScroll()
  const scaleX = useSpring(scrollYProgress, {
    stiffness: 100,
    damping: 30,
    restDelta: 0.001
  })

  return (
    <>
      <motion.div
        style={{ scaleX }}
        className="fixed top-0 left-0 right-0 h-1 bg-blue-500 origin-left z-50"
      />
      
      <div className="h-[200vh] p-8">
        <h1 className="text-4xl font-bold mb-4">Scroll down</h1>
        <p>The blue bar at the top tracks scroll progress.</p>
      </div>
    </>
  )
}

How it works

Scroll animations use the scroll position to drive animations:
1

Track scroll position

Use useScroll() to get scroll progress values (0 to 1).
2

Transform values

Convert scroll progress to animation values using useTransform().
3

Apply to elements

Link transformed values to element properties via the style prop.
4

Add smoothing

Use useSpring() to smooth jerky scroll movements.

Scroll hooks

useScroll

Tracks scroll position and returns progress values

useInView

Detects when elements enter/leave the viewport

useTransform

Maps scroll values to animation properties

useSpring

Adds smooth spring physics to scroll values

Variations

Staggered scroll reveals

Reveal items with delay as they enter view:
import { motion } from 'framer-motion'
import { useInView } from 'framer-motion'
import { useRef } from 'react'

function StaggeredReveal({ items }) {
  const ref = useRef(null)
  const isInView = useInView(ref, { once: true })

  return (
    <div ref={ref}>
      {items.map((item, i) => (
        <motion.div
          key={i}
          initial={{ opacity: 0, x: -50 }}
          animate={isInView ? { opacity: 1, x: 0 } : { opacity: 0, x: -50 }}
          transition={{ delay: i * 0.1, duration: 0.5 }}
          className="p-4 mb-4 bg-white rounded-lg shadow"
        >
          {item}
        </motion.div>
      ))}
    </div>
  )
}

Scroll-driven counter

Animate numbers based on scroll:
import { motion, useScroll, useTransform, useSpring } from 'framer-motion'
import { useRef, useEffect, useState } from 'react'

function ScrollCounter({ target = 100 }) {
  const ref = useRef(null)
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ['start end', 'end start']
  })
  
  const count = useTransform(scrollYProgress, [0, 1], [0, target])
  const springCount = useSpring(count, { stiffness: 100, damping: 30 })
  const [displayCount, setDisplayCount] = useState(0)

  useEffect(() => {
    return springCount.onChange(v => setDisplayCount(Math.round(v)))
  }, [])

  return (
    <div ref={ref} className="h-screen flex items-center justify-center">
      <motion.div className="text-6xl font-bold text-blue-500">
        {displayCount}
      </motion.div>
    </div>
  )
}

Horizontal scroll sections

Scroll horizontally through sections:
import { motion, useScroll, useTransform } from 'framer-motion'
import { useRef } from 'react'

function HorizontalScroll() {
  const ref = useRef(null)
  const { scrollYProgress } = useScroll({ target: ref })
  const x = useTransform(scrollYProgress, [0, 1], ['0%', '-75%'])

  return (
    <div ref={ref} className="h-[400vh] relative">
      <div className="sticky top-0 h-screen overflow-hidden">
        <motion.div style={{ x }} className="flex h-full">
          {[1, 2, 3, 4].map((i) => (
            <div
              key={i}
              className="min-w-full h-full flex items-center justify-center bg-gradient-to-r from-blue-500 to-purple-500 text-white text-6xl font-bold"
            >
              Section {i}
            </div>
          ))}
        </motion.div>
      </div>
    </div>
  )
}

Image reveal on scroll

Reveal image as user scrolls:
import { motion, useScroll, useTransform } from 'framer-motion'
import { useRef } from 'react'

function ImageReveal({ src }) {
  const ref = useRef(null)
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ['start end', 'end start']
  })

  const scale = useTransform(scrollYProgress, [0, 0.5, 1], [0.8, 1, 0.8])
  const opacity = useTransform(scrollYProgress, [0, 0.3, 0.7, 1], [0, 1, 1, 0])

  return (
    <div ref={ref} className="h-screen flex items-center justify-center">
      <motion.img
        src={src}
        style={{ scale, opacity }}
        className="max-w-2xl rounded-lg shadow-2xl"
      />
    </div>
  )
}

Example from the playground

From the ReactAnimations component:
import { useScroll, useSpring, motion } from 'framer-motion'

function ScrollAnimation() {
  const { scrollYProgress } = useScroll()
  const scaleX = useSpring(scrollYProgress, {
    stiffness: 100,
    damping: 30,
    restDelta: 0.001
  })

  return (
    <motion.div
      style={{ scaleX }}
      className="fixed top-0 left-0 right-0 h-1 bg-blue-500 origin-left"
    />
  )
}

Best practices

Prevent elements from re-animating when scrolling back up.
const isInView = useInView(ref, { once: true })
Trigger animations slightly before elements enter viewport.
const isInView = useInView(ref, { margin: '-100px' })
Apply spring physics to scroll-driven values for smoother motion.
const smoothProgress = useSpring(scrollYProgress, {
  stiffness: 100,
  damping: 30
})
Use will-change CSS property and avoid animating expensive properties.
<motion.div style={{ willChange: 'transform' }} />
Disable scroll animations for users with motion sensitivity.
const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches

Common use cases

  • Scroll progress indicators
  • Fade-in content on scroll
  • Parallax backgrounds
  • Sticky headers with animations
  • Horizontal scrolling galleries
  • Scroll-driven counters
  • Image reveals
  • Section transitions

Performance tips

Scroll animations can impact performance on lower-end devices. Test thoroughly and provide fallbacks.
// Good - GPU accelerated
<motion.div style={{ y, opacity }} />

// Avoid - causes layout recalc
<motion.div style={{ top: scrollY, height: scrollHeight }} />

Advanced: Section-based scroll

Animate based on specific section visibility:
import { motion, useScroll, useTransform } from 'framer-motion'
import { useRef } from 'react'

function SectionScroll() {
  const ref = useRef(null)
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ['start end', 'end start']
  })

  const backgroundColor = useTransform(
    scrollYProgress,
    [0, 0.5, 1],
    ['#3B82F6', '#8B5CF6', '#EC4899']
  )

  return (
    <motion.div
      ref={ref}
      style={{ backgroundColor }}
      className="h-screen flex items-center justify-center text-white text-4xl font-bold"
    >
      Color changes as you scroll
    </motion.div>
  )
}

Fade in

Basic opacity transitions

Drag gesture

Interactive drag animations

Slide in

Directional animations