Skip to main content

Command Palette

Search for a command to run...

Master React Micro-Interactions with Framer Motion

Functional UI is only half the battle. The real magic lies in Micro-interactions—subtle animations that turn a generic website into a premium product. Let’s learn how to implement them in React using Framer Motion, from absolute basics to advanced layouts.

Updated
3 min read
A
Full-Stack Web Developer passionate about architecting scalable web applications and clean code. Formally trained via Programming Hero, specializing in Next.js, Node.js, and PostgreSQL. Experienced in agile team collaboration, project management, and transforming complex backend logic into fluid user experiences. Writing about web development, databases, and modern tech stacks.

Why Framer Motion Over Traditional CSS?

While CSS transitions and keyframes are great for simple loops, they quickly become messy when handling complex component lifecycles (like when an element unmounts from the DOM).

Framer Motion is built specifically for React. It uses a declarative syntax, meaning you just tell it what state you want to animate to based on props or state, and it handles the physics-based transitions under the hood.

Setting Up the Environment

First, let’s install the package into your React/Next.js project:

Bash

npm install framer-motion

1. The Core Concept: The motion Component

Framer Motion works by wrapping standard HTML elements with a motion proxy. This unlocks specialized props like initial, animate, whileHover, and whileTap.

Let’s build a high-fidelity interactive button that uses spring physics instead of rigid time durations:

JavaScript

import { motion } from "framer-motion";

export const InteractiveButton = () => {
  return (
    <motion.button
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      whileHover={{ 
        scale: 1.05, 
        backgroundColor: "#4f46e5",
        boxShadow: "0px 10px 20px rgba(79, 70, 229, 0.3)" 
      }}
      whileTap={{ scale: 0.95 }}
      transition={{ 
        type: "spring", 
        stiffness: 400, 
        damping: 15 
      }}
      className="px-6 py-3 bg-indigo-600 text-white font-semibold rounded-lg shadow-md transition-colors"
    >
      Explore Projects
    </motion.button>
  );
};

Breaking Down the Props:

  • initial: The starting state before the component mounts. Here, it starts hidden (opacity: 0) and pushed down slightly (y: 20).

  • animate: The target state when the component renders. It fades in and glides up smoothly.

  • whileHover & whileTap: Built-in event listeners. Notice how we smoothly scale up on hover, change the color, add a glowing box-shadow, and shrink it slightly on click for a satisfying tactile feel.

  • transition: Instead of a boring linear duration, we used a spring visual. stiffness controls the speed/force, and damping controls the bounciness.

2. Advanced: Orchestrating Lists (Variants)

When a recruiter loads your portfolio, stagger-animating your project cards looks highly professional. We achieve this using Variants. Variants allow parent elements to coordinate animations for their children.

Here is how to create a staggered grid container:

JavaScript

import { motion } from "framer-motion";

// 1. Define animation layouts
const containerVariants = {
  hidden: { opacity: 0 },
  show: {
    opacity: 1,
    transition: {
      staggerChildren: 0.2, // Delays each child by 0.2s
    },
  },
};

const cardVariants = {
  hidden: { opacity: 0, scale: 0.9, y: 30 },
  show: { 
    opacity: 1, 
    scale: 1, 
    y: 0,
    transition: { type: "spring", stiffness: 300, damping: 20 }
  },
};

// 2. Implement components
export const ProjectGrid = ({ projects }) => {
  return (
    <motion.div
      variants={containerVariants}
      initial="hidden"
      animate="show"
      className="grid grid-cols-1 md:grid-cols-3 gap-6 p-6"
    >
      {projects.map((project) => (
        <motion.div
          key={project.id}
          variants={cardVariants}
          className="bg-white dark:bg-zinc-900 p-6 rounded-xl shadow-lg border border-gray-100 dark:border-zinc-800"
        >
          <h3 className="text-xl font-bold mb-2">{project.title}</h3>
          <p className="text-gray-600 dark:text-gray-400">{project.description}</p>
        </motion.div>
      ))}
    </motion.div>
  );
};

Why Recruiters Care

When a technical interviewer reviews this code in your GitHub repository, it shows them that you understand modern UI orchestration, declarative layout cycles, and advanced user experience principles.