Using Intersection Observer and Framer Motion for Scroll-Based Animations in React

Simple fade-in when a component enters the viewport via horizontal scroll.

Framer Motion is such an intuitive and easy-to-use animation library that I’m honestly a little surprised it doesn’t have built-in tracking for whether a motion component is in the viewport. It can tell us whether a component is present in the React tree or how far we’ve scrolled through an element/viewport, but none of those are really ideal for triggering animations at the exact moment an element enters the viewport. Luckily this functionality is pretty easy to implement using react-intersection-observer. I’ll also assume you already have a project set up and won’t get into horizontal scrolling but here’s the tutorial I followed.

Start by importing { motion } from framer-motion and the { InView } component from react-intersection-observer (there’s also a hook version useInView but because I’m tracking multiple elements I found the component approach to be simpler). Turn the first component you’d like to animate into a motion component and define its initial (hidden) state and animate (value you’d like to animate to) state, along with any other props you’d like to include.

Currently this will animate as soon as it mounts (aka before it’s in the viewport).

Now, in order to track this component, we’ll wrap it inside an InView component and pass the ref and inView props to the wrapped motion component. The ref is to target the component we’re animating, and inView will return a boolean we can use to trigger the animation. We can also give the InView component a threshold (number between 0 and 1) to tell it how much of the element should be in the viewport before inView returns true (in the example below it would be 25%).

The motion.img component is now animating to full opacity when inView is true, and back to zero opacity when inView returns false again. But what if we wanted it to keep its opacity so that it doesn’t fade away after scrolling past it? Luckily this is pretty simple to achieve — just add triggerOnce to your InView component (you could also just pass null as the “false” value for animate but that seems a little messier). We could also repeat this same approach for every page element we want to animate on scroll. Beyond that, have fun experimenting with your animate/transition values and thanks for reading!




UI/UX Developer in Austin, TX

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Bumps in the Coding Bootcamp Road — Maximum update depth exceeded

Different ways of writing a functions in funtional programming language

Exercise with React, Recoil, Nodejs and Express: starting from scratch — intro — part 1

Best MERN Stack Courses In 2022

Developing Better Node.js Developers

[[Prototype]] vs __proto__ vs .prototype in Javascript

A confused man tries to work out what prototype means.

The RxJS concatenation strategies, pt 4/4

Hello I am Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Brad Carter

Brad Carter

UI/UX Developer in Austin, TX

More from Medium

Blog Portfolio Essay

Easydoc: bridging the language barrier between health care providers and patients- a UX case study.

“Special IDO” Luxury brand 3.0 Kalissa Paris 🔥💎♾

UNI announces ‘Worth the wait’  — Architecture Competition to Design challenge for a dental clinic…