Use Tailwind CSS with Variants in Framer Motion



Combining Tailwind CSS for styling and Framer Motion for animations can be challenging because Tailwind uses static utility classes, while Framer Motion relies on dynamic animation variants. This can create conflicts and make it difficult to integrate both smoothly in a React project.

Our task is to find a way to use Tailwind's styles with Framer Motion's animations without conflicts, allowing smooth integration in a React project.

Approaches

we will cover different approaches to integrate Tailwind CSS with FramerMotion, explained step by step for smooth styling and animations in your React project.

Steps to Setup

Before we start with the approaches, you need to set up your environment and install the required dependencies. This includes setting up Tailwind CSS and Framer Motion in your React project.

First, create a new React app using create-react-app:

npx create-react-app tailwind-motion-demo
cd tailwind-motion-demo

After creating the app, install React 18 and ReactDOM 18 to avoid version conflicts:

npm install react@18 react-dom@18

Now, install Tailwind CSS and its dependencies:

npm install -D tailwindcss postcss autoprefixer

Run the following command to initialize the Tailwind CSS configuration:

npx tailwindcss init

Open tailwind.config.js and modify the content array to look like this:

module.exports = {
    content: [
        './src/**/*.{js,jsx,ts,tsx}', // Ensure Tailwind scans all your React files
    ],
    theme: {
        extend: {},
    },
    plugins: [],
}

Open src/index.css (create it if it doesn't exist) and add the following Tailwind directives:

/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

Make sure src/index.css is imported into src/index.js:

import './index.css'; // This imports the Tailwind CSS into your React project

Next, install Framer Motion for animations:

npm install framer-motion

Using className with Framer Motion's motion components

This approach uses Tailwind CSS for styling and Framer Motion's motion components for animations. Here are the steps we followed:

First, we create or update MyComponent.js in /src/components/:

import { motion } from 'framer-motion';

const MyComponent = () => {
    return (
        <motion.div
            className="bg-purple-500 p-6 rounded-lg shadow-2xl border border-fuchsia-900"
            animate={{ scale: 1.2 }}   // Animation: scale to 1.2
            transition={{ duration: 0.5 }} // Duration of animation: 0.5 seconds
        >
            <h1 className="text-white text-xl">Hello, Framer Motion!</h1>
        </motion.div>
    );
};

export default MyComponent;

Next, we use MyComponent in App.js:

import React from 'react';
import MyComponent from './components/MyComponent';

function App() {
    return (
        <div className="App flex justify-center items-center h-screen bg-gray-100">
            <MyComponent />
        </div>
    );
}

export default App;

Then, we run the following command in the terminal:

npm start

Output

Output of Framer motion

Using Variants with Conditional Classes

In this approach, we'll animate the component based on a condition, dynamically changing the Tailwind classes according to the animation state. Steps we have taken include:

First, we update MyComponent.js to include variants:

import { motion } from 'framer-motion';
import { useState } from 'react';

const MyComponent = () => {
    const [isVisible, setIsVisible] = useState(false);

    const variants = {
        hidden: { opacity: 0, scale: 0 },
        visible: { opacity: 1, scale: 1 }
    };

    // Conditional class for background
    const boxClass = isVisible ? 'bg-green-500' : 'bg-red-500';

    return (
        <div className="flex justify-center items-center h-screen">
            <motion.div
                // Apply dynamic class
                className={`p-6 rounded-lg ${boxClass}`} 
                variants={variants}
                animate={isVisible ? 'visible' : 'hidden'}
                transition={{ duration: 0.5, ease: 'easeInOut' }}
            >
                <h1 className="text-white text-center">
                    Animated Box&
                lt;/h1>
            </motion.div>

        {/* Toggle Button */}
            <div className="mt-5">
                <button
                    onClick={() => setIsVisible(!isVisible)}
                    className="px-2 py-2 bg-purple-500 
                            text-white rounded-lg"
                >
                    Toggle Visibility
                </button>
            </div>
        </div>
    );
};

Now, use this MyComponent in App.js as before.

Finally, run the application by opening the terminal and executing the following command:

npm start

Output

output gif

Using dynamic variants with Tailwind classes

In this approach, we will use Framer Motion's animation states to change Tailwind classes, allowing the component's appearance to update smoothly based on its state. Below are the steps we took:

We start by updating the MyComponent.js file to handle dynamic animation states and apply Tailwind classes based on the current animation state.

import { motion } from 'framer-motion';
import { useState } from 'react';

const MyComponent = () => {
  const [isClicked, setIsClicked] = useState(false);

  const variants = {
    start: { backgroundColor: 'rgb(34, 197, 94)' },  // green-500
    end: { backgroundColor: 'rgb(239, 68, 68)' }    // red-500
  };

  return (
    <motion.div
      className="p-6 rounded-lg"
      animate={isClicked ? 'end' : 'start'}
      variants={variants}
      transition={{ duration: 0.5 }}
      onClick={() => setIsClicked(!isClicked)}
    >
    <h1 className="text-white">
        Click to Change Color
    </h1>
    </motion.div>
  );
};

export default MyComponent;

Once we have updated MyComponent.js, we can use it in your App.js file as follows:

import React from 'react';
// Import MyComponent
import MyComponent from './MyComponent';

function App() {
  return (
    <div className="App">
    {/* Use MyComponent in App.js */}
      <MyComponent />
    </div>
  );
}

export default App;

Run the app to see the background color change when clicking the component.

npm start

Output

output gif

Using Layout Animations with Tailwind

In this approach, we will use Framer Motion's layout prop to animate layout changes like resizing or repositioning an element. Below are the steps we took:

Update MyComponent.js to use the layout prop, which makes the component resize when the isLarge state changes after clicking.

import { motion } from 'framer-motion';  // Import Framer Motion for layout animations
import { useState } from 'react';  // Import useState to manage the size state

const MyComponent = () => {
    // Track whether the component is large
    const [isLarge, setIsLarge] = useState(false);  

    return (
        <motion.div
            // Change width based on state
            className={`p-6 rounded-lg ${isLarge ? 'w-64' : 'w-32'} bg-blue-500`}  
            layout  // Enable layout animation
            // Toggle size on click
            onClick={() => setIsLarge(!isLarge)}  
            // Set the duration of the resize animation
            transition={{ duration: 0.5 }}
        >
            <h1 className="text-white">Click to Resize</h1>
        </motion.div>
    );
};

export default MyComponent;

Then, we run the following command in the terminal:

npm start

Output

Output of fourth approach
Updated on: 2025-01-07T08:58:30+05:30

151 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements