← Back
Micro-interaction

Ripple Effect

Material-design ripple burst spawned at the exact click position inside the button.

click any button to see the ripple

RippleEffect.tsx77 lines
"use client";

import { useRef } from "react";

function RippleBtn({
  children,
  className = "",
}: {
  children: React.ReactNode;
  className?: string;
}) {
  const ref = useRef<HTMLButtonElement>(null);

  function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
    const btn = ref.current;
    if (!btn) return;
    const { left, top, width, height } = btn.getBoundingClientRect();
    const size = Math.max(width, height) * 2;
    const x    = e.clientX - left - size / 2;
    const y    = e.clientY - top  - size / 2;

    const ripple = document.createElement("span");
    ripple.style.cssText = `
      position: absolute;
      border-radius: 50%;
      width: ${size}px;
      height: ${size}px;
      left: ${x}px;
      top: ${y}px;
      background: rgba(255,255,255,0.35);
      animation: ripple 600ms ease-out forwards;
      pointer-events: none;
    `;
    btn.appendChild(ripple);
    setTimeout(() => ripple.remove(), 700);
  }

  return (
    <button
      ref={ref}
      onClick={handleClick}
      className={`relative overflow-hidden select-none cursor-pointer ${className}`}
    >
      {children}
    </button>
  );
}

export default function RippleEffect() {
  return (
    <div className="flex flex-col items-center gap-8 py-10 w-full">
      <div className="flex flex-wrap gap-4 justify-center">
        <RippleBtn className="px-7 py-3 rounded-full bg-violet-600 text-white font-semibold text-sm">
          Click me
        </RippleBtn>
        <RippleBtn className="px-7 py-3 rounded-full bg-sky-500 text-white font-semibold text-sm">
          Ripple
        </RippleBtn>
        <RippleBtn className="px-7 py-3 rounded-full bg-emerald-500 text-white font-semibold text-sm">
          Effect
        </RippleBtn>
        <RippleBtn className="px-7 py-3 rounded-full bg-amber-500 text-white font-semibold text-sm">
          Try me
        </RippleBtn>
      </div>

      <RippleBtn className="w-full max-w-sm py-4 rounded-2xl bg-gradient-to-r from-violet-600 to-indigo-600 text-white font-semibold">
        Wide button — click anywhere
      </RippleBtn>

      <p className="text-xs text-zinc-400 dark:text-zinc-600 font-mono">
        click any button to see the ripple
      </p>
    </div>
  );
}

More in Micro-interaction