Ripple Button

FREE

An interactive button component with a beautiful ripple effect animation on click.

Demo

Exclusive Launch 50% offspots left: 0 / 50
3D Cube - happy face3D Cube - Happy Face
$39.99

$19.99

Payment once and get lifetime access
Access to all animations
All updates
All animations
No subscriptions

Installation

1

Install framer-motion

pnpm add framer-motion
2

Copy and paste the following component into your project:

Add this component RippleButton.tsx to your project.

ripple-button.tsx
"use client";
import { motion } from "framer-motion";
import React, { useState, useEffect, useRef } from "react";

const RippleButton = () => {
  const [ripples, setRipples] = useState<
    {
      id: number;
      x: number;
      y: number;
      size: number;
    }[]
  >([]);

  // Track timeout IDs to clear them on unmount
  const timeoutRefs = useRef<NodeJS.Timeout[]>([]);

  // Clear all pending timeouts on component unmount
  useEffect(() => {
    return () => {
      timeoutRefs.current.forEach((timeout) => clearTimeout(timeout));
    };
  }, []);

  const createRipple = (e: React.MouseEvent<HTMLButtonElement>) => {
    const button = e.currentTarget as HTMLButtonElement;
    const rect = button.getBoundingClientRect();
    const size = Math.max(rect.width, rect.height);
    const x = e.clientX - rect.left - size / 2;
    const y = e.clientY - rect.top - size / 2;

    const newRipple = {
      x,
      y,
      size,
      id: Date.now(),
    };

    setRipples((prev) => [...prev, newRipple]);

    // Store timeout ID and clear it from the ref when it executes
    const timeoutId = setTimeout(() => {
      setRipples((prev) => prev.filter((ripple) => ripple.id !== newRipple.id));
      // Remove this timeout from the ref array
      timeoutRefs.current = timeoutRefs.current.filter(
        (id) => id !== timeoutId,
      );
    }, 600);

    // Add timeout ID to ref for cleanup
    timeoutRefs.current.push(timeoutId);
  };

  return (
    <button
      className="relative overflow-hidden rounded-lg bg-white px-6 py-3 text-black"
      onClick={createRipple}
    >
      <span className="relative z-10">Click for Ripple</span>
      {ripples.map((ripple) => (
        <motion.span
          key={ripple.id}
          className="pointer-events-none absolute rounded-full bg-black opacity-20"
          style={{
            left: ripple.x,
            top: ripple.y,
            width: ripple.size,
            height: ripple.size,
          }}
          initial={{ scale: 0, opacity: 0.3 }}
          animate={{ scale: 2, opacity: 0 }}
          transition={{ duration: 0.6 }}
        />
      ))}
    </button>
  );
};

export default RippleButton;
3

Start using the ripple button in your components.

Usage Example

Customization

You can customize the RippleButton by modifying the component code:


// Custom styling example
<button
className="relative overflow-hidden rounded-lg bg-blue-500 px-8 py-4 text-white font-semibold hover:bg-blue-600 transition-colors"
onClick={createRipple}
>
<span className="relative z-10">Custom Button</span>
{ripples.map((ripple) => (
  <motion.span
    key={ripple.id}
    className="pointer-events-none absolute rounded-full bg-white opacity-30"
    style={{
      left: ripple.x,
      top: ripple.y,
      width: ripple.size,
      height: ripple.size,
    }}
    initial={{ scale: 0, opacity: 0.3 }}
    animate={{ scale: 2, opacity: 0 }}
    transition={{ duration: 0.6 }}
  />
))}
</button>

Features

  • Interactive Ripple Effect: Creates a ripple animation at the click position
  • Multiple Ripples: Supports multiple simultaneous ripples
  • Automatic Cleanup: Ripples are automatically removed after animation
  • Customizable: Easy to modify colors, size, and styling
  • Smooth Animation: Uses Framer Motion for smooth transitions
  • Responsive: Works on all screen sizes

Properties

The current RippleButton component is self-contained and doesn't accept props. However, you can easily extend it to accept custom props:

PropertyTypeDefaultDescription
childrenReactNode"Click for Ripple"Button text content
classNamestring""Additional CSS classes
rippleColorstring"bg-black"Color of the ripple effect
rippleOpacitynumber0.2Opacity of the ripple effect
durationnumber0.6Duration of the ripple animation
onClickfunction-Additional click handler

Advanced Usage

For more control over the ripple effect, you can create a customizable version:


interface RippleButtonProps {
children: React.ReactNode;
className?: string;
rippleColor?: string;
rippleOpacity?: number;
duration?: number;
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
}

const CustomizableRippleButton: React.FC<RippleButtonProps> = ({
children,
className = "",
rippleColor = "bg-black",
rippleOpacity = 0.2,
duration = 0.6,
onClick,
}) => {
// ... component implementation
};