Back to blog
AnimationReact

Mastering Framer Motion: From Basics to Complex Animations

05 Feb 20266 min read

Mastering Framer Motion: From Basics to Complex Animations

Introduction

Most developers treat animation as an afterthought — something sprinkled on at the end with a few CSS transitions and called done. The problem is that CSS transitions, while useful, don't compose well. The moment you need animations that respond to state, chain sequences together, or coordinate across multiple components, you're hacking around the limitations of the cascade.

Framer Motion changes that equation completely. It's a production-ready animation library for React that handles everything from simple fade-ins to complex orchestrated sequences — and it does it with an API that actually makes sense. After using it across several projects, I've come to think of animation as a first-class part of my component design rather than a layer on top.

This guide goes from the fundamentals to the patterns I actually use in production.


Core Concepts

The motion Component

Everything in Framer Motion starts with the motion primitive. You take any HTML element and prefix it with motion. — that's it. The element gains superpowers:

import { motion } from 'framer-motion';

// A div that animates
<motion.div
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ duration: 0.4 }}
>
  Hello, world
</motion.div>

initial is where the element starts. animate is where it ends up. transition controls timing. That's the entire mental model — everything else is built on top of these three props.

Variants: The Real Power

For anything beyond a single element, variants are how you keep your animations clean and maintainable. Instead of inline objects, you define a named map of animation states:

const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1, // delay between each child
      delayChildren: 0.2,
    },
  },
};

const itemVariants = {
  hidden: { opacity: 0, y: 20 },
  visible: { opacity: 1, y: 0 },
};

Then use them on parent and children:

<motion.ul variants={containerVariants} initial="hidden" animate="visible">
  {items.map((item) => (
    <motion.li key={item.id} variants={itemVariants}>
      {item.label}
    </motion.li>
  ))}
</motion.ul>

The critical insight: child elements inherit initial and animate from their parent. You don't need to repeat those props on every <motion.li>. And staggerChildren orchestrates them automatically — no manual delays.

The useAnimation Hook

When you need programmatic control over animations — triggered by async events, user actions, or external state — useAnimation gives you an imperative handle:

import { motion, useAnimation } from 'framer-motion';
import { useEffect } from 'react';

function PulseButton() {
  const controls = useAnimation();
If this helped you, leave a ❤️

Comments

Coming Soon

Comment section will be available shortly

Have thoughts on this? Reach out — I'd love to chat.

Get in Touch