Animation Without the CSS Grunt Work
Animation is one of the quickest ways to make an interface feel polished, but getting it right with raw CSS often means wrestling with keyframes, timing functions, and browser quirks. Framer Motion removes most of that friction for React developers. It gives you a low-level API wrapped around production-ready animation primitives, so you can build gesture-driven, timed, or scroll-triggered effects without becoming a CSS specialist.
The core building block is the motion component. You create one by prefixing any regular HTML or SVG element with motion — for example, motion.h1 or motion.div. These components accept props that describe how they should animate. The most basic is animate, which takes an object of CSS properties and values. When the component mounts, it animates from its current style to the values you define.
For example, to make an h1 slide in on load, you'd set the x and y properties inside the animate prop. Without explicit units, Framer Motion calculates values in pixels. You can override this by passing a string: animate={{x: "20rem", y: "-20rem"}}.
By default, a motion component starts from its inherited style and transitions to the animate state. To control the starting point, use the initial prop. Together, these two props define the "before mount" and "after mount" states. Want the element to enter from the left? Set that in initial, and it will slide in when it appears.
You're not limited to a single animation. Pass an array of values to animate through sequential keyframes. The transition prop governs how each step happens — you can specify duration, delay, and the type of easing or spring you want.
Cleaning Up With Variants
When several components need coordinated animations, passing the same props around gets repetitive. The variants prop solves this by letting you extract animation definitions into standalone objects. Each variant object holds named sets of values, and you reference those names in the initial and animate props.
Variants also support propagation down the DOM. If you set up a parent motion component with variants keys that match its children's variants, the parent passes the current animation state through automatically. The children no longer need explicit initial and animate props — the parent orchestrates it. This keeps the code clean and makes it easy to coordinate complex multi-element animations.
Gesture Animations
Framer Motion has built-in gesture detection for hover, tap, pan, and drag. The whileHover prop starts an animation while the user's pointer is over the component. A good example of this is an icon shop, where hovering changes the way an icon card presents itself.
In that pattern, you might have a card container that slides in on mount and a card component with two variant objects for its states. Before hover, the icon inside the card is invisible and pushed down. On hover, the card scales up and the icon slides into place with its opacity restored. Using variants, you can define the hovered and non-hovered states without tying yourself in knots.
Parent-Child Timing
Variants aren't just good for organizing code — they enable fine-grained control over animation sequence order. You can define a real relationship between parent and children using properties inside the transition object.
Set when: "beforeChildren" to make the parent finish its animation before any child starts. Add staggerChildren to create a delay between each child's animation. This is ideal for an animated navbar: when the menu opens, the links can fade and slide in one after another rather than all at once.
You'll often drive these states with React state and toggle them on click. A button's animation can change based on a boolean, and the navbar can conditionally animate its open and closed positions via initial, animate, and a corresponding variant set.
Exit Animations
Mount animations are straightforward, but what about when a component leaves the DOM? By default, it disappears instantly. AnimatePresence changes that. Wrapping your conditional renders in this component gives you access to the exit prop on motion children.
For a modal, you'd wrap the modal's conditional in AnimatePresence, then use the exit prop on the modal box and its content to define how they fade or scale away. The exit animation can mirror the enter effect, giving a polish to closing interactions that many animation libraries skip.
Scroll-Triggered Effects
Animations that fire only when a user reaches them add life to longer pages. Framer Motion's useAnimation hook gives you programmatic access to start and stop animations on a motion component. It returns a controls object with start and stop methods, which you can wire up to other libraries.
Pair it with react-intersection-observer's useInView hook to watch a ref and get an inView boolean. In a useEffect, listen for when the element enters the viewport, then call controls.start with the visible variant. The motion component starts hidden, and the intersection observer triggers its entrance animation just when it scrolls into sight. It's a small amount of glue code that unlocks a whole category of page-level motion.
Putting useCycle to Work in a Hero Banner
To demonstrate how useCycle works in a practical setting, we can build a hero banner that cycles between two distinct animation states. This hook acts much like useState, giving us a value and a function to change that value, but with a focus on transitioning between predefined animations.
In the hero component, we define three variant sets: H1Variants, TextVariants, and BannerVariants. The key is the BannerVariants object, where we specify two separate animation states: animationOne and animationTwo.
import React, { useEffect } from "react";
import { useCycle } from "framer-motion";
import { Container, H1, HeroSection, Banner, TextBox } from "./Styles";
import { ReactComponent as BannerIllustration } from "./bighead.svg";
const H1Variants = {
initial: { y: -200, opacity: 0 },
animate: { y: 0, opacity: 1, transition: { delay: 1 } },
};
const TextVariants = {
initial: { x: 400 },
animate: { x: 0, transition: { duration: 0.5 } },
};
const BannerVariants = {
animationOne: { x: -250, opacity: 1, transition: { duration: 0.5 } },
animationTwo: {
y: [0, -20],
opacity: 1,
transition: { yoyo: Infinity, ease: "easeIn" },
},
};
When we destructure useCycle, we get the current animation state (named animation here) and a function to advance to the next state (cycleAnimation). We initialise the hook with the two animation objects defined in BannerVariants. To trigger the state change, we call cycleAnimation after a delay of two seconds, wrapped inside a useEffect.
const [animation, cycleAnimation] = useCycle("animationOne", "animationTwo");
useEffect(() => {
setTimeout(() => {
cycleAnimation();
}, 2000);
}, []);
Once everything is wired up, the Banner component is assigned its variants. On mount, it will slide in from the right based on animationOne. After the two-second timeout, cycleAnimation fires, swapping the active state to animationTwo for the next phase of the transition.
<div className="App">
<Container>
<H1 variants={H1Variants} initial="initial" animate="animate">
Cool Hero Section Anmiation
</H1>
<HeroSection>
<TextBox variants={TextVariants} initial="initial" animate="animate">
Storage shed, troughs feed bale manure, is garden wheat oats at
augers. Bulls at rose garden cucumbers mice sunflower wheat in pig.
Chainsaw foal hay hook, herbs at combine harvester, children is
mallet. Goat goose hen horse. Pick up truck livestock, pets and
storage shed, troughs feed bale manure, is garden wheat oats at
augers. Lamb.
</TextBox>
<Banner variants={BannerVariants} animate={animation}>
<BannerIllustration />
</Banner>
</HeroSection>
</Container>
</div>
This small example showcases how easily Framer Motion can handle sequenced, state-driven animations without manual event listeners or complex state tracking.
Beyond the Basics
These examples cover the foundational concepts of Framer Motion—from variants and transitions to orchestration hooks like useCycle. They give a clear idea of the breadth of animation possibilities available directly through the library's declarative API. For more advanced techniques, such as gesture handling, layout animations, or scroll-linked effects, the official documentation is the best starting point to dive deeper.
Resources for Further Exploration
- Framer Motion Api Docs, Framer Motion
- react-intersection-observer, npm
- Framer Motion for React, NetNinja



