CC
0 XP
0

Chapter 3: Polish & Design

Animations with Motion

build5 min

Animations with Motion

CSS transitions handle simple state changes (hover, focus). For more complex animations — list items entering and exiting, page transitions, layout shifts — we need a proper animation library.

Install Motion

"Install motion (the new name for framer-motion) and add entrance animations to the todo list. Each todo item should fade in and slide up when added. When a todo is deleted, it should fade out and shrink. The list should use AnimatePresence to handle items entering and leaving. Stagger the initial load so items appear one by one."

Terminal
$npm install motion
💡Info

Framer Motion was renamed to just "motion" in its latest major version. It's the most popular animation library for React, with a declarative API that works naturally with React components. If you Google for help, most tutorials and Stack Overflow answers still reference "framer-motion" — the API is the same, just the package name and import path changed (from framer-motion to motion/react).

How Motion works

Motion uses a component-based API. You replace standard HTML elements with motion components:

import { motion, AnimatePresence } from 'motion/react'
 
// A div that fades in on mount
<motion.div
  initial={{ opacity: 0, y: 10 }}
  animate={{ opacity: 1, y: 0 }}
  exit={{ opacity: 0, height: 0 }}
  transition={{ duration: 0.2 }}
>
  {todo.text}
</motion.div>

Animating the todo list

The key pieces:

  1. AnimatePresence wraps the list to detect when items are added or removed
  2. motion.div replaces the todo item container
  3. initial defines the starting state (invisible, shifted down)
  4. animate defines the end state (visible, in position)
  5. exit defines how items leave (fade out, collapse)
  6. layout prop tells Motion to animate layout changes smoothly
<AnimatePresence>
  {todos.map((todo, index) => (
    <motion.div
      key={todo.id}
      initial={{ opacity: 0, y: 10 }}
      animate={{ opacity: 1, y: 0 }}
      exit={{ opacity: 0, x: -10, height: 0 }}
      transition={{
        duration: 0.2,
        delay: index * 0.03, // stagger effect
      }}
      layout
    >
      <TodoItem todo={todo} />
    </motion.div>
  ))}
</AnimatePresence>
Tip

The layout prop is magic. It tells Motion to animate any layout changes smoothly. When an item is deleted and others move up to fill the gap, they'll glide into position instead of jumping. This single prop makes the list feel alive.

Stagger on initial load

When the page first loads, items appear one by one instead of all at once:

"Add a staggered entrance animation on the initial page load. Each todo item should appear 30ms after the previous one, creating a cascading effect. After the initial load, new items should just fade in individually."

Keep animations subtle

⚠️Warning

The best UI animations are ones users barely notice. If someone says "nice animation," it might be too much. The goal is that removing the animation would make the app feel worse, but having it doesn't draw attention. Keep durations between 150-300ms and movements under 20px.

Motion and Server Components

Since Motion uses React hooks internally, it needs to be used in Client Components:

"Make sure the animated todo list component is marked as a Client Component with 'use client'. Keep the rest of the page as Server Components where possible."

Your todo list now feels alive — items flow in and out gracefully, the layout adjusts smoothly, and the whole experience feels fluid and modern.