Introducing AnimateIn: A New React Utility Component for Animations with Tailwind and CSS

John Polacek
4 min readJan 15, 2024

--

<AnimateIn/> Demo Page Animation

I’ve been using the same animation pattern in my projects for awhile now to animate elements onto the screen. In its simplest form, you would have an element styled with 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 re-usable 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 a 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 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 re-usable 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.

--

--

John Polacek

Engineering Manager at Howl. I love brass tacks and the nitty gritty. Ideas are great. Executing is better.