Skip to main content

Live demo

The fade in animation is one of the most common and versatile animations in web design. It creates a smooth transition by gradually increasing an element’s opacity from 0 to 1, making content appear gracefully rather than popping in suddenly.
Fade animations work best when combined with subtle transforms like scale or translate for added depth.

Complete code example

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

export default function FadeInDemo() {
  const [isVisible, setIsVisible] = useState(true)

  return (
    <div>
      <AnimatePresence mode="wait">
        {isVisible && (
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -20 }}
            transition={{ duration: 0.5, ease: 'easeOut' }}
            className="p-6 bg-blue-500 text-white rounded-lg"
          >
            I fade in and out smoothly
          </motion.div>
        )}
      </AnimatePresence>
      
      <button
        onClick={() => setIsVisible(!isVisible)}
        className="mt-4 px-4 py-2 bg-gray-900 text-white rounded"
      >
        Toggle Element
      </button>
    </div>
  )
}

How it works

The fade in animation operates on two primary principles:
1

Opacity transition

The element’s opacity gradually changes from 0 (invisible) to 1 (fully visible), creating the fade effect.
2

Optional transform

Adding a subtle vertical translation (20px) creates depth and makes the animation feel more natural.
3

Timing function

Using ease-out or similar curves makes the animation start fast and slow down, which feels more natural.

Key properties

opacity

Controls transparency: 0 = invisible, 1 = fully visible

transform

Adds subtle movement (optional but recommended)

duration

0.3-0.5s is ideal for most fade animations

timing

ease-out or similar curves feel most natural

Variations

Fade in on scroll

Trigger fade animations when elements enter the viewport:
import { motion } from 'framer-motion'
import { useInView } from 'framer-motion'
import { useRef } from 'react'

function ScrollFadeIn({ children }) {
  const ref = useRef(null)
  const isInView = useInView(ref, { once: true })

  return (
    <motion.div
      ref={ref}
      initial={{ opacity: 0, y: 50 }}
      animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 50 }}
      transition={{ duration: 0.6 }}
    >
      {children}
    </motion.div>
  )
}

Staggered fade in

Fade in multiple elements with a delay:
import { motion } from 'framer-motion'

const container = {
  hidden: { opacity: 0 },
  show: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1
    }
  }
}

const item = {
  hidden: { opacity: 0, y: 20 },
  show: { opacity: 1, y: 0 }
}

function StaggeredList({ items }) {
  return (
    <motion.div
      variants={container}
      initial="hidden"
      animate="show"
    >
      {items.map((text, i) => (
        <motion.div key={i} variants={item}>
          {text}
        </motion.div>
      ))}
    </motion.div>
  )
}

Fade with scale

Combine fade with scale for emphasis:
<motion.div
  initial={{ opacity: 0, scale: 0.8 }}
  animate={{ opacity: 1, scale: 1 }}
  exit={{ opacity: 0, scale: 0.8 }}
  transition={{ duration: 0.4 }}
>
  Content
</motion.div>

Best practices

Fade animations should enhance the experience, not distract. Use durations between 0.3-0.5 seconds.
Adding a small vertical movement (10-20px) makes fades feel more polished and directional.
Always check for prefers-reduced-motion and provide instant transitions for accessibility.
const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches

const duration = prefersReducedMotion ? 0 : 0.5
  • ease-out: Best for entrances (starts fast, ends slow)
  • ease-in: Best for exits (starts slow, ends fast)
  • ease-in-out: Balanced, works for both

Common use cases

  • Modal and dialog entrances
  • Toast notifications
  • Loading content reveals
  • Image galleries
  • Page transitions
  • Tooltip appearances

Performance tips

Animating opacity is GPU-accelerated and performs well. Avoid animating other properties like color or background-color alongside opacity unless necessary.
// Good - GPU accelerated
<motion.div animate={{ opacity: 1, transform: 'translateY(0)' }} />

// Avoid - Forces repaints
<motion.div animate={{ opacity: 1, backgroundColor: '#fff' }} />

Slide in

Directional entrance animations

Scale

Grow and shrink effects

Scroll animation

Trigger on scroll position

Build docs developers (and LLMs) love