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
Framer Motion
React Spring
CSS
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 >
)
}
import { useSpring , animated } from '@react-spring/web'
import { useState } from 'react'
export default function FadeInDemo () {
const [ isVisible , setIsVisible ] = useState ( true )
const fadeProps = useSpring ({
opacity: isVisible ? 1 : 0 ,
transform: isVisible ? 'translateY(0px)' : 'translateY(20px)' ,
config: { tension: 280 , friction: 60 }
})
return (
< div >
< animated.div
style = { {
... fadeProps ,
padding: '1.5rem' ,
background: '#3B82F6' ,
color: 'white' ,
borderRadius: '0.5rem'
} }
>
I fade in with spring physics
</ animated.div >
< button
onClick = { () => setIsVisible ( ! isVisible ) }
className = "mt-4 px-4 py-2 bg-gray-900 text-white rounded"
>
Toggle Element
</ button >
</ div >
)
}
import { useState } from 'react'
import './fade.css'
export default function FadeInDemo () {
const [ isVisible , setIsVisible ] = useState ( true )
return (
< div >
{ isVisible && (
< div className = "fade-in p-6 bg-blue-500 text-white rounded-lg" >
I fade in with CSS
</ div >
) }
< button
onClick = { () => setIsVisible ( ! isVisible ) }
className = "mt-4 px-4 py-2 bg-gray-900 text-white rounded"
>
Toggle Element
</ button >
</ div >
)
}
@keyframes fade-in {
0% {
opacity : 0 ;
transform : translateY ( 20 px );
}
100% {
opacity : 1 ;
transform : translateY ( 0 );
}
}
.fade-in {
animation : fade-in 0.5 s ease-out ;
}
How it works
The fade in animation operates on two primary principles:
Opacity transition
The element’s opacity gradually changes from 0 (invisible) to 1 (fully visible), creating the fade effect.
Optional transform
Adding a subtle vertical translation (20px) creates depth and makes the animation feel more natural.
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
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
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