I’ve been using the same animation pattern in my projects for a while now to animate elements onto the screen. In its simplest form, you would have an element styled with the opacity of zero, then change the styling to have an opacity of one with a CSS transition of a second.
We can build on top of that by adding other properties that transition, changing the duration, adding a delay, or setting custom easing.
<AnimateIn/> is a reusable React component that I’ve made to drop in whenever I want to quickly add some animation effects to my projects. A simple utility component, it combines CSS Animation with Tailwind classes to create fluid, eye-catching animations with minimal effort.
Let’s take a look at how it is used. After importing the component, define from
and to
states with Tailwind classes. Wrap the target element within <AnimateIn/> to see the animation come to life.
import AnimateIn from '../animation/AnimateIn';
<AnimateIn
from="opacity-0 scale-90"
to="opacity-100 scale-100"
duration={500}
>
<YourComponent />
</AnimateIn>
Here’s a slightly more complex example that uses more properties to animate in a headline and subtitle.
import AnimateIn from '../animation/AnimateIn';
<header>
<AnimateIn
as="h1"
from="opacity-0 translate-y-32"
to="opacity-100 translate-y-0"
delay={500}
duration={300}
className="text-4xl"
style={{transitionTimingFunction:"cubic-bezier(0.25, 0.4, 0.55, 1.4)"}}
>
My Big Headline
</AnimateIn>
<AnimateIn
as="h2"
from="opacity-0 scale-0"
to="opacity-100 scale-100"
delay={800}
duration={500}
className="text-lg"
>
This is a subtitle below the headline
</AnimateIn>
</header>
In the headline example, <AnimateIn/> is used to create a sliding effect combined with a fade-in. Here’s how each property contributes to the animation:
as
property: By setting as="h1"
, we tell AnimateIn to render the animation as an <h1>
element.
from
and to
properties: The from
property starts the headline off-screen (translate-y-32
, moving it 32 units down) and invisible (opacity-0
). The to
property then brings the headline to its final position (back to translate-y-0
) and makes it fully visible (opacity-100
).
duration
property: The animation is set to begin immediately with no delay and runs for a quick 300ms.
className
property: The className="text-4xl"
applies Tailwind's utility class to set the font size, making the headline prominently stand out.
style
property: The custom transitionTimingFunction
(cubic-bezier(0.25, 0.4, 0.55, 1.4)
) adds a unique ease to the animation, giving it a bounce-like effect.
The subtitle uses a different set of animations to complement the headline, creating a cohesive visual flow.
as
property: Here, as="h2"
renders the component as an <h2>
element, suitable for a subtitle.
from
and to
properties: The subtitle starts scaled down to zero (scale-0
) and invisible (opacity-0
), then scales up to its natural size (scale-100
) and becomes fully visible (opacity-100
). This scaling effect, paired with a fade-in, adds depth to the animation.
delay
and duration
properties: The subtitle also starts after an 800ms delay so that it begins after the headline has fully animated. This staggered approach ensures that each element gets its moment of focus.
className
property: The className="text-lg"
sets the subtitle's font size, making it smaller than the headline but still significant.
To better understand what’s happening, let’s look at the source code for <AnimateIn/> on Github:
<AnimateIn/> uses a useState
hook to initialize the animation state with the from
property, which should be one or more Tailwind utility classes, setting the stage for the animation’s starting point before any animation takes place.
The first useEffect
hook in the component is for respecting user preferences for reduced motion. By listening to the (prefers-reduced-motion: reduce)
media query, the animation behavior is based on the user’s system settings. If reduced motion is preferred, the animation is skipped entirely, directly setting the animation state to the to
property, allowing for an accessible experience.
The second useEffect
hook is where the animation logic resides. If the user has not indicated a preference for reduced motion, the component sets a timer that changes the animation state from the initial from
value to the final to
value after the specified delay. This transition creates the visual effect of animation.
The cleanup function of this hook (the return statement) clears the timer, preventing potential memory leaks such as if the component unmounts before the animation is completed.
The React.createElement
function call is the component’s rendering mechanism. It dynamically creates an HTML element based on the as
prop, allowing the use of the component across different HTML elements. The className
is constructed using thecn
function as popularized by shadcn, which combines Tailwind’s utility classes, the custom className
passed as a prop, and the current animation state. This dynamic class assignment is what applies the desired styles and transitions to the element.
Additionally, there is astyle
attribute that can be passed in to directly set styling properties on the animation container. The transitionDuration
is set based on the duration
prop, but it intelligently switches to 0ms
if the user prefers reduced motion, effectively disabling the animation while maintaining the component’s functionality.
If you’d like to use <AnimateIn/> in your own project and it already uses shadcn, then you already have everything you need, just download AnimateIn.tsx and add it to your components.
Otherwise, you’ll want to install Tailwind as well as mxcn
the helpful utility for merging tailwind classes.
Like shadcn, <AnimateIn/> is meant to be a reusable component that you can copy and paste into your apps and customize to your needs. The code is yours.
Also, I’ve put together a nice demo page for playing around with creating different animations with <AnimateIn/> at animate-in.vercel.app.
Also published here