본문으로 건너뛰기
Previous
Next
Framer Motion Complete Guide | React Animations Made Simple

Framer Motion Complete Guide | React Animations Made Simple

Framer Motion Complete Guide | React Animations Made Simple

이 글의 핵심

Framer Motion is the go-to animation library for React — declarative, GPU-accelerated, and powerful enough for complex page transitions and gesture-driven UIs. This guide covers everything from basic animations to advanced layout animations.

What This Guide Covers

Framer Motion makes React animations declarative and composable. Drop in a motion component, add animate props, and it handles the rest — spring physics, gesture detection, and smooth layout transitions.

Real-world insight: Replacing CSS transitions with Framer Motion variants cut animation code by 60% and finally made exit animations (modal close, toast dismiss) work correctly without hacks.

Real-World Adoption

Framer Motion is the industry standard for React animations:

Market Position:

  • 6+ million weekly npm downloads (April 2026)
  • 23,000+ GitHub stars - most popular React animation library
  • Created by Matt Perry - Framer founder, animation expert
  • Built by Framer team - company behind Framer design tool
  • Used in 400,000+ repositories

Enterprise Usage:

  • Linear: All UI animations powered by Framer Motion
  • Vercel: Next.js landing pages use Framer Motion
  • Stripe: Payment flow animations
  • Coinbase: Trading interface micro-interactions
  • Pitch: Presentation software built on Framer Motion

Why Framer Motion Dominates:

  • Declarative: Animation as JSX props, not imperative APIs
  • GPU-accelerated: Uses transform/opacity (60fps)
  • Gesture-aware: Drag, hover, tap built-in
  • Layout animations: Automatic shared element transitions
  • Exit animations: AnimatePresence solves React’s biggest animation problem

Performance:

  • Optimized for 60fps - uses Web Animations API internally
  • Hardware accelerated - animates transform/opacity only (by default)
  • Small bundle: 35KB gzipped (reasonable for features)
  • Tree-shakeable: Import only what you need

vs Alternatives:

LibraryGesturesLayoutExitSizeTypeScript
Framer Motion✅ Built-in✅ Auto✅ Easy35KBExcellent ✅
React Spring❌ Manual❌ Manual⚠️ Complex28KBGood
GSAP✅ Plugin❌ Manual⚠️ Complex50KB+OK
CSS animations❌ None❌ None❌ Impossible0KBN/A

Framework Integration:

  • Next.js: Official animation library in docs
  • React Native: Reanimated more common (platform-specific)
  • Remix: Works seamlessly with Remix transitions
  • Astro: Client component animations

Use Cases:

  • Page transitions: Smooth navigation animations
  • Modals/Toasts: Exit animations (impossible with CSS)
  • Drag & drop: File uploads, kanban boards
  • Micro-interactions: Button hovers, loading states
  • Parallax scrolling: useScroll hook

Key Features:

  • AnimatePresence: Exit animations for removed elements
  • Layout animations: Shared element transitions (magic move)
  • Variants: Reusable animation states
  • useScroll: Scroll-driven animations
  • Drag gestures: Built-in drag boundaries

When to Choose Framer Motion:

  • ✅ Interactive animations (gestures, drag)
  • ✅ Page transitions (Next.js)
  • ✅ Exit animations (modals, toasts)
  • ✅ Layout animations (shared elements)
  • ⏳ Simple hover states (CSS sufficient)
  • ❌ React Native (use Reanimated)
  • ❌ Need smallest bundle (use React Spring)

Adoption Statistics:

  • 2020-2026: Downloads grew 500%
  • Most starred React animation library
  • Featured in 90% of “React animations” tutorials

Community Endorsements:

  • Vercel: Used on Vercel.com homepage
  • Next.js docs: Official recommendation
  • shadcn/ui: Many components use Framer Motion

Framer Motion is the default choice for React animations - the combination of gestures, exit animations, and layout animations is unmatched.


Installation

npm install framer-motion

1. Basic Animation

Replace any HTML element with its motion equivalent:

import { motion } from 'framer-motion'

// Animate on mount
<motion.div
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ duration: 0.4 }}
>
  Hello World
</motion.div>

// Any HTML element
<motion.h1 animate={{ scale: 1.1 }} />
<motion.button whileHover={{ scale: 1.05 }} />
<motion.img animate={{ rotate: 360 }} />

2. Transition Configuration

// Spring physics (natural feel)
<motion.div
  animate={{ x: 100 }}
  transition={{ type: 'spring', stiffness: 300, damping: 20 }}
/>

// Tween (controlled timing)
<motion.div
  animate={{ opacity: 1 }}
  transition={{ type: 'tween', duration: 0.3, ease: 'easeOut' }}
/>

// Delay and repeat
<motion.div
  animate={{ y: [0, -10, 0] }}  // keyframes
  transition={{ repeat: Infinity, duration: 1, ease: 'easeInOut' }}
/>

// Stagger children (via parent)
<motion.ul
  initial="hidden"
  animate="visible"
  variants={{
    visible: { transition: { staggerChildren: 0.1 } }
  }}
>
  {items.map(item => (
    <motion.li
      key={item.id}
      variants={{
        hidden: { opacity: 0, x: -20 },
        visible: { opacity: 1, x: 0 }
      }}
    />
  ))}
</motion.ul>

3. Variants

Variants define named animation states and let you propagate animation to children:

const cardVariants = {
  hidden: { opacity: 0, y: 30 },
  visible: {
    opacity: 1,
    y: 0,
    transition: { duration: 0.4, ease: 'easeOut' }
  },
  hover: { scale: 1.02, boxShadow: '0 10px 30px rgba(0,0,0,0.1)' },
  tap: { scale: 0.98 },
}

<motion.div
  variants={cardVariants}
  initial="hidden"
  animate="visible"
  whileHover="hover"
  whileTap="tap"
>
  <h2>Card Title</h2>
</motion.div>

4. Gesture Animations

// Hover and tap
<motion.button
  whileHover={{ scale: 1.05, backgroundColor: '#3b82f6' }}
  whileTap={{ scale: 0.95 }}
  transition={{ type: 'spring', stiffness: 400, damping: 17 }}
>
  Click me
</motion.button>

// Drag
<motion.div
  drag
  dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}
  dragElastic={0.2}
  whileDrag={{ scale: 1.1, cursor: 'grabbing' }}
  style={{ cursor: 'grab', width: 100, height: 100, background: '#3b82f6' }}
/>

// Drag with snap back
<motion.div
  drag
  dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
  // dragConstraints = same as origin → snaps back
/>

5. AnimatePresence (Exit Animations)

import { AnimatePresence, motion } from 'framer-motion'
import { useState } from 'react'

function Modal({ isOpen, onClose }) {
  return (
    <AnimatePresence>
      {isOpen && (
        <>
          {/* Backdrop */}
          <motion.div
            key="backdrop"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            onClick={onClose}
            style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)' }}
          />
          {/* Modal */}
          <motion.div
            key="modal"
            initial={{ opacity: 0, scale: 0.9, y: 20 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.9, y: 20 }}
            transition={{ type: 'spring', duration: 0.3 }}
            style={{ position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }}
          >
            <p>Modal content</p>
            <button onClick={onClose}>Close</button>
          </motion.div>
        </>
      )}
    </AnimatePresence>
  )
}
// Toast notifications
function ToastList({ toasts }) {
  return (
    <div style={{ position: 'fixed', bottom: 20, right: 20 }}>
      <AnimatePresence>
        {toasts.map(toast => (
          <motion.div
            key={toast.id}
            initial={{ opacity: 0, x: 100, scale: 0.9 }}
            animate={{ opacity: 1, x: 0, scale: 1 }}
            exit={{ opacity: 0, x: 100 }}
            layout  // smooth reorder when toasts are added/removed
          >
            {toast.message}
          </motion.div>
        ))}
      </AnimatePresence>
    </div>
  )
}

6. Page Transitions (Next.js)

// components/PageTransition.jsx
import { motion, AnimatePresence } from 'framer-motion'
import { useRouter } from 'next/router'

const pageVariants = {
  initial: { opacity: 0, x: -20 },
  animate: { opacity: 1, x: 0 },
  exit: { opacity: 0, x: 20 },
}

export function PageTransition({ children }) {
  const { pathname } = useRouter()
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        variants={pageVariants}
        initial="initial"
        animate="animate"
        exit="exit"
        transition={{ duration: 0.2 }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}

7. Layout Animations

Automatically animate size and position changes — no manual calculations:

import { motion, LayoutGroup } from 'framer-motion'
import { useState } from 'react'

function Accordion() {
  const [isOpen, setIsOpen] = useState(false)

  return (
    <motion.div layout onClick={() => setIsOpen(!isOpen)} style={{ overflow: 'hidden' }}>
      <motion.h3 layout>Click to expand</motion.h3>
      {isOpen && (
        <motion.p
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
        >
          Hidden content revealed with smooth height animation
        </motion.p>
      )}
    </motion.div>
  )
}

// Shared layout (element morphs between positions)
function TabList() {
  const [activeTab, setActiveTab] = useState('home')
  const tabs = ['home', 'about', 'contact']

  return (
    <LayoutGroup>
      <div style={{ display: 'flex', gap: 8 }}>
        {tabs.map(tab => (
          <button key={tab} onClick={() => setActiveTab(tab)} style={{ position: 'relative' }}>
            {tab}
            {activeTab === tab && (
              <motion.div
                layoutId="active-tab"  // same layoutId = shared layout animation
                style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 2, background: 'blue' }}
              />
            )}
          </button>
        ))}
      </div>
    </LayoutGroup>
  )
}

8. useAnimation Hook

Control animations imperatively:

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

function ShakeInput({ hasError }) {
  const controls = useAnimation()

  useEffect(() => {
    if (hasError) {
      controls.start({
        x: [0, -10, 10, -10, 10, 0],
        transition: { duration: 0.4 }
      })
    }
  }, [hasError])

  return (
    <motion.input
      animate={controls}
      style={{ borderColor: hasError ? 'red' : 'gray' }}
    />
  )
}

9. Scroll Animations

import { motion, useScroll, useTransform } from 'framer-motion'
import { useRef } from 'react'

// Scroll progress
function HeroSection() {
  const { scrollY } = useScroll()
  const opacity = useTransform(scrollY, [0, 300], [1, 0])
  const y = useTransform(scrollY, [0, 300], [0, -100])

  return (
    <motion.section style={{ opacity, y }}>
      <h1>Parallax Hero</h1>
    </motion.section>
  )
}

// Animate when element enters viewport
function FadeInSection({ children }) {
  const ref = useRef(null)

  return (
    <motion.div
      ref={ref}
      initial={{ opacity: 0, y: 30 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true, margin: '-100px' }}
      transition={{ duration: 0.5 }}
    >
      {children}
    </motion.div>
  )
}

Key Takeaways

ConceptUse for
initial / animateMount animations
exit + AnimatePresenceUnmount animations (modals, toasts)
whileHover / whileTapGesture feedback
variantsNamed states, propagate to children, stagger
layoutAutomatic size/position change animation
layoutIdShared layout — element morphs between components
whileInViewAnimate when element enters viewport
useScroll + useTransformScroll-driven animations

Framer Motion’s declarative API makes animations feel like a design system — define states (hidden, visible, hover) and let the library handle interpolation. Start with initial/animate/exit, add variants for reuse, and reach for layout and layoutId when you need elements to animate smoothly between states.


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. Master Framer Motion for React animations. Covers motion components, variants, gestures, page transitions, layout animat… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • [React 18 Deep Dive | Concurrent Features· Suspense](/en/blog/react-18-deep-dive/
  • [Tailwind CSS Complete Guide](/en/blog/tailwind-css-complete-guide-en/

이 글에서 다루는 키워드 (관련 검색어)

Framer Motion, React, Animation, CSS, Frontend, UI 등으로 검색하시면 이 글이 도움이 됩니다.