Motion
Back to framer.com
DocumentationAnimation
Scroll
Motion
Back to framer.com
Design and publish your first free site today.
Getting started
  • Introduction
  • Examples
Animation
  • Overview
  • Layout
  • Gestures
  • Scroll
  • Transition
Components
  • motion
  • AnimatePresence
  • LayoutGroup
  • LazyMotion
  • MotionConfig
  • Reorder
Motion values
  • Overview
  • useMotionValueEvent
  • useMotionTemplate
  • useScroll
  • useSpring
  • useTime
  • useTransform
  • useVelocity
  • useWillChange
Utilities
  • animate
  • transform
  • useAnimationControls
  • useAnimationFrame
  • useDragControls
  • useInView
  • useReducedMotion
3D
  • Introduction
  • LayoutCamera
  • LayoutOrthographicCamera
  • MotionCanvas
Guides
  • Accessibility
  • Reduce bundle size
  • Upgrade guides
Community
  • GitHub
  • Discord

Scroll animations

How to create scroll-linked and scroll-triggered animations in Framer Motion.

There are two predominant types of scroll animations, both of which can be achieved with Framer Motion.

Scroll-linked animations are when the progress of an animation is directly tied to scroll progress. Scroll-triggered animations are when a normal animation is triggered when an element enters or leaves the viewport.

#Scroll-linked animations

Scroll-linked animations are created using motion values and the useScroll hook.

import { motion, useScroll } from "framer-motion"
function Component() {
const { scrollYProgress } = useScroll();
return (
<motion.div style={{ scaleX: scrollYProgress }} />
)
}

Read the full useScroll docs to discover how to track element scroll, element positions within the viewport, create parallax effects and more.

#Scroll-triggered animations

Scroll-triggered animations are normal animations that start when an element enters or leaves the viewport.

The whileInView prop can be used to create scroll-triggered animations by defining a set of properties and, optionally, a transition, to animate to when the element is in view.

<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
/>

Note: There's currently a bug in Chrome where IntersectionObserver doesn't work correctly with SVG elements.

#whileInView: VariantLabels | TargetAndTransition

Properties or variant label to animate to while the element is in view.

#viewport: ViewportOptions

An object of viewport options that define how the viewport is detected.

<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
/>

#onViewportEnter(entry): void

Callback that triggers when the element enters the viewport. Provides the IntersectionObserverEntry with details of the intersection event, or null if the browser doesn't support IntersectionObserver.

#onViewportLeave(entry): void

Callback that triggers when the element leaves the viewport. Provides the IntersectionObserverEntry with details of the intersection event. Will never be called if the browser doesn't support IntersectionObserver.

#Viewport options

#once: boolean

If true, once the element has entered the viewport it will remain in the whileInView state. No further viewport callbacks will be triggered.

<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
/>

#root: RefObject<Element>

By default, the element will be considered within the viewport when it enters the window viewport.

Pass a ref to both an ancestor element and to viewport.root to use that ancestor element as the measured viewport instead.

function Component() {
const scrollRef = useRef(null)
return (
<div ref={scrollRef} style={{ overflow: "scroll" }}>
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ root: scrollRef }}
/>
</div>
)
}

#margin: string

A margin to add to the viewport when detecting whether the element has entered it.

Defaults to "0px". A single value can be used to add a margin on every side, e.g. "200px". Or, multiple values can be defined to assign a margin to each axis in the order of top/right/bottom/left, e.g. "0px -20px 0px 100px".

#amount: "some" | "all" | number

Defaults to "some", this option defines the amount of the element that has to intersect with the viewport in order for it to be considered within view.

#fallback: boolean

Framer Motion uses IntersectionObserver to detect viewport enter/exit. If IntersectionObserver is not available in the current browser, onViewportEnter will be fired and whileInView set when the component first mounts.

By setting fallback: false this behaviour will be disabled. No viewport events will be fired and whileInView will never be set.

#Example

#Page scroll progress

#Spring smoothing

#Element scroll progress

#Track element position

#Parallax

#3D

#Scroll velocity

PreviousGesturesNextTransition
On this page
  • Scroll-linked animations
  • Scroll-triggered animations
  • Viewport options
  • Example
  • Page scroll progress
  • Spring smoothing
  • Element scroll progress
  • Track element position
  • Parallax
  • 3D
  • Scroll velocity

Copyright © 2022 Framer B.V.

  • Security
  • Terms of Service
  • Privacy Statement