DEV Community

Cover image for Framer Motion Animated Hero with Reactjs
Codex
Codex

Posted on

Framer Motion Animated Hero with Reactjs

Create a Stunning Animated Hero with React.js and Framer Motion

Bring your web designs to life with Framer Motion! In this blog post, weโ€™ll explore how to create a visually stunning animated hero section using React.js and Framer Motion. Learn how smooth transitions, interactive animations, and colorful designs can enhance your website's user experience. You can download the source code for free.. ๐Ÿ‘‡

๐ŸŽฅ What Youโ€™ll Learn

๐Ÿ’ป Animated Hero Section

Weโ€™ll craft an engaging hero section with:

  • Smooth transitions to captivate your audience.
  • Interactive animations that respond to user interactions.

โœจ Why Framer Motion?

If youโ€™ve ever wanted to:

  • Animate elements in your React projects.
  • Trigger animations on scroll, hover, or page load.
  • Enhance your user experience with motion designโ€”then this is the perfect tutorial for you!

Framer Motion makes it easy to integrate powerful animations into your React components, turning static designs into dynamic experiences.


๐Ÿš€ Step-by-Step Guide

Step 1: Set Up Your React Project

Start with a React project. If you donโ€™t already have one:

yarn create vite my-project
cd my-project

yarn add -D tailwindcss postcss autoprefixer
yarn tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

Install Framer Motion

yarn add framer-motion
Enter fullscreen mode Exit fullscreen mode

Setup for Tailwindcss

/** @type {import('tailwindcss').Config} */
export default {
  mode: 'jit', 
  content: [
    './index.html',
    './src/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      fontFamily: {
        clash: ['Cabin Sketch"', 'sans-serif'], 
      },
      fontSize: {
        '10xl': '10rem', 
        '11xl': '12rem',
        '12xl': '14rem', 
        '13xl': '16rem', 
      },
      colors: {
        orange: '#FFEFDA',
        pineapple: '#E4FFC0',
      },
    },
  },
  plugins: [],
}
Enter fullscreen mode Exit fullscreen mode
/* index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

html, body {
    font-family: "Cabin Sketch", sans-serif;
  }
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the AnimatedImage Component

Inside the src folder, create a new file called Hero.tsx:

import React from 'react';
import { motion, MotionValue } from 'framer-motion';

interface AnimationProps {
  x?: MotionValue<number>;
  y?: MotionValue<number>;
  rotateX?: MotionValue<number>;
  rotateY?: MotionValue<number>;
  rotate?: MotionValue<number>;
  rotateZ?: MotionValue<number>;
  scale?: MotionValue<number>;
}

interface AnimatedImageProps {
  src: string;
  alt: string;
  width: number;
  height: number;
  className: string;
  animationProps: AnimationProps;
}

const AnimatedImage: React.FC<AnimatedImageProps> = ({
  src,
  alt,
  width,
  height,
  className,
  animationProps,
}) => {
  return (
    <motion.div style={{ perspective: 1000, ...animationProps }}>
      <img src={src} alt={alt} width={width} height={height} className={className} />
    </motion.div>
  );
};

export default AnimatedImage;
Enter fullscreen mode Exit fullscreen mode

Step 3: Create A container component for AnimatedImage

AnimateImageContainer.tsx

import React from 'react';
import AnimatedImage from './AnimatedImage';
import { useTransform } from 'framer-motion';

interface AnimatedImagesContainerProps {
  currentFlavor: string;
  flavors: { [key: string]: any };
  mouseX: any;
  mouseY: any;
}

const AnimatedImagesContainer: React.FC<AnimatedImagesContainerProps> = ({
  currentFlavor,
  flavors,
  mouseX,
  mouseY,
}) => {
  return (
    <>
      {/* Can Image */}
      <div className="absolute inset-0 flex items-center justify-center">
        <AnimatedImage
          src={flavors[currentFlavor].can}
          alt={`Tarragon ${currentFlavor} Can`}
          width={200}
          height={300}
          className="object-contain relative left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1"
          animationProps={{
            x: useTransform(mouseX, [0, 1], [-30, 30]),
            y: useTransform(mouseY, [0, 1], [-30, 30]),
            rotateY: useTransform(mouseX, [0, 1], [-15, 15]),
            rotateX: useTransform(mouseY, [0, 1], [15, -15]),
            rotateZ: useTransform(mouseX, [0, 1], [-10, 10]),
          }}
        />
      </div>

      {/* Slice Image */}
      <div className="relative top-1/3 left-1/2">
        <AnimatedImage
          src={flavors[currentFlavor].slice}
          alt={`${currentFlavor} Slice`}
          width={250}
          height={250}
          className="object-contain absolute top-1/4 right-3/4"
          animationProps={{
            x: useTransform(mouseX, [0, 1], [-50, 50]),
            y: useTransform(mouseY, [0, 1], [-50, 50]),
            rotate: useTransform(mouseX, [0, 1], [-25, 25]),
            rotateZ: useTransform(mouseX, [0, 1], [-10, 10]),
            scale: useTransform(mouseY, [0, 1], [0.9, 1.1]),
          }}
        />
      </div>

      {/* Leaf Image */}
      <div className="relative top-1/3 left-1/2">
        <AnimatedImage
          src={currentFlavor === 'pineapple' ? flavors['pineapple'].leafImage : flavors['orange'].leafImage}
          alt="Leaf"
          width={100}
          height={100}
          className="object-contain relative bottom-1/2 right-1/4"
          animationProps={{
            x: useTransform(mouseX, [0, 1], [40, -40]),
            y: useTransform(mouseY, [0, 1], [-40, 40]),
            rotate: useTransform(mouseX, [0, 1], [-5, 15]),
            rotateY: useTransform(mouseX, [0, 1], [-10, 10]),
            scale: useTransform(mouseY, [0, 1], [0.9, 1.1]),
          }}
        />
      </div>

      {/* Additional Slice Image */}
      <div className="relative top-1/2 left-3/4">
        <AnimatedImage
          src={currentFlavor === 'pineapple' ? flavors['pineapple'].sliceImage : flavors['orange'].sliceImage}
          alt="Slice"
          width={250}
          height={250}
          className="object-contain relative bottom-3/4 right-1/2"
          animationProps={{
            x: useTransform(mouseX, [0, 1], [40, -40]),
            y: useTransform(mouseY, [0, 1], [-40, 40]),
            rotateZ: useTransform(mouseX, [1, 0], [-1, 1]),
            scale: useTransform(mouseY, [0, 1], [0.9, 1.1]),
          }}
        />
      </div>
    </>
  );
};

export default AnimatedImagesContainer;

Enter fullscreen mode Exit fullscreen mode

Step 4: Integrate the all Hero Component

Import and use the Hero component in App.tsx:

import React, { useState, useRef, useEffect } from 'react';
import { useMotionValue } from 'framer-motion';
import { motion } from 'framer-motion'; // Import motion from framer-motion
import Header from './components/Header';
import FlavorDisplay from './components/FlavorDetail';
import AnimatedImagesContainer from './components/AnimatedImagesContainer';
import SwitchFlavorButton from './components/Button';
import images from './assets';

// Define your types
type Flavor = 'orange' | 'pineapple';

interface FlavorDetails {
  name: string;
  color: string;
  textColor: string;
  can: string;
  slice: string;
  leafImage: string;
  sliceImage: string;
}

interface Flavors {
  [key: string]: FlavorDetails;
}

// Define flavors with additional images
const flavors: Flavors = {
  orange: {
    name: 'Orange',
    color: '#FFEFDA',
    textColor: '#FFD399',
    can: images.orange.can,
    slice: images.orange.slices[0],
    leafImage: images.orange.leaf,
    sliceImage: images.orange.slices[1],
  },
  pineapple: {
    name: 'Pineapple',
    color: '#E4FFC0',
    textColor: '#B7EC73',
    can: images.pineapple.can,
    slice: images.pineapple.slices[0],
    leafImage: images.pineapple.slices[2], // you can use leaf images as well
    sliceImage: images.pineapple.slices[1],
  },
};

const App: React.FC = () => {
  const [currentFlavor, setCurrentFlavor] = useState<Flavor>('orange');
  const containerRef = useRef<HTMLDivElement>(null);
  const mouseX = useMotionValue(0);
  const mouseY = useMotionValue(0);

  const switchFlavor = () => {
    setCurrentFlavor((prev) => (prev === 'orange' ? 'pineapple' : 'orange'));
  };

  useEffect(() => {
    const handleMouseMove = (event: MouseEvent) => {
      const container = containerRef.current;
      if (container) {
        const { left, top, width, height } = container.getBoundingClientRect();
        const x = event.clientX - left;
        const y = event.clientY - top;
        mouseX.set(x / width);
        mouseY.set(y / height);
      }
    };

    window.addEventListener('mousemove', handleMouseMove);
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
    };
  }, [mouseX, mouseY]);

  return (
    <motion.div
      ref={containerRef}
      className="relative w-full h-screen overflow-hidden" 
      style={{ backgroundColor: flavors[currentFlavor].color }}
      key={currentFlavor} // Keying by flavor for triggering re-mounting and animations
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }} // Animation on exit
      transition={{ duration: 0.5 }} // Animation duration
    >
      <Header title="Tarragon" navItems={['Products', 'About', 'Insights']} />

      <FlavorDisplay
        flavorName={flavors[currentFlavor].name}
        textColor={flavors[currentFlavor].textColor}
      />

      <AnimatedImagesContainer
        currentFlavor={currentFlavor}
        flavors={flavors}
        mouseX={mouseX}
        mouseY={mouseY}
      />

      <SwitchFlavorButton onClick={switchFlavor} />
    </motion.div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ฅ Enhance the Experience

  • Trigger animations on scroll with React Intersection Observer.
  • Use Framer Motion Variants for reusable animation logic.
  • Explore SVG animations to add unique visual elements.

๐ŸŒ Try It Yourself

Free Download the full project here ๐Ÿ‘‰ Free Download from Buymecoffee

Github ๐Ÿ‘‰ Github

Inspired Figma Design ๐Ÿ‘‰ Figma


๐ŸŽฌ Watch the Video

For a completed video on YouTube:

Framer Motion Animated Hero with React.js


๐Ÿ–ฅ About Framer Motion

Framer Motion is a powerful library for React animations. It provides:

  • Declarative animations with React components.
  • Smooth transitions and interactive effects.
  • Simple yet highly customizable APIs.

๐Ÿ’ก Conclusion

Motion design transforms static websites into engaging experiences. With Framer Motion, you can easily create animations that enhance your React projects.

Weโ€™d love to hear your feedback! Let us know in the comments how youโ€™d use these animations in your own projects.


๐Ÿ‘€ Like, Share, and Follow

Stay tuned for more tutorials on React.js, web design, and animations. Donโ€™t forget to subscribe to our channel and share this guide with fellow developers!


๐Ÿ”— Useful Links


#React #FramerMotion #WebDesign #Animation #ReactJS #WebDevelopment #HeroSection #FrontendDevelopment #ReactAnimations

Top comments (0)