Sponsored

How to create a ripple effect in react

Jasser Mark Arioste

Jasser Mark Arioste

How to create a ripple effect in react

In one of my projects, I needed a way to add ripple effect to buttons since I was using a UI library with no ripple effect. In this tutorial, I'll show you how to add a ripple effect using react hooks. The final result can be previewed below:

There are a couple of things to note. First is we will will be using typescript in this tutorial. Lastly, we will only have one dependency which is usehooks-ts since we will be using the useDebounce hook later on to remove the ripples after a few seconds has passed.

Sponsored

Steps to create a ripple effect#

there are three main things that you need when creating ripple effect in react.

  1. Button with position:relative and overflow:hidden
  2. a ripple keyframe css
  3. a react hook that creates ripples: useRipple hook
Sponsored

Step 1: Button with position relative#

Let's create a simple button component for now. well add the functionality later. If you're using css libraries like tailwindcss or daisyUI, you can use the classes to create a button as well

#components/Button.tsx

import React, { useRef } from "react";


const Button = () => {

  return (
    <button
      style={{
        padding: 16,
        backgroundColor: "#1e293b",
        color: "white",
        border: "none",
        borderRadius: 8,
        cursor: "pointer",
        position: "relative",
        overflow: "hidden",
      }}
    >
      Button
    </button>
  );
};

export default Button;
1234567891011121314151617181920212223242526

Output:

Simple button with no functionaltiy
Sponsored

Step 2: Add a @keyframe ripple css#

Let's add this to our css file. Basically, you just need to scale and reduce the opacity to 0.

#style.css
@keyframes ripple {
  to {
    transform: scale(4);
    opacity: 0;
  }
}
1234567
Sponsored

Step 3: Create a useRipple hook.#

Let's now create the useRipple hook that gives life to our button. But first we'll install usehooks-ts

yarn add usehooks-ts
1

Create a file called useRipple.tsx. Please read through the comments because they are important!

//hooks/useRipple.tsx

import React, { useEffect, useState } from "react";
import { useDebounce } from "usehooks-ts";

/**
 * This hook accepts a ref to any element and adds a click event handler that creates ripples when click
 */
const useRipple = <T extends HTMLElement>(ref: React.RefObject<T>) => {
  //ripples are just styles that we attach to span elements
  const [ripples, setRipples] = useState<React.CSSProperties[]>([]);

  useEffect(() => {
    //check if there's a ref
    if (ref.current) {
      const elem = ref.current;

      //add a click handler for the ripple
      const clickHandler = (e: MouseEvent) => {
        //calculate the position and dimensions of the ripple.
        //based on click position and button dimensions
        var rect = elem.getBoundingClientRect();
        var left = e.clientX - rect.left;
        var top = e.clientY - rect.top;
        const height = elem.clientHeight;
        const width = elem.clientWidth;
        const diameter = Math.max(width, height);
        setRipples([
          ...ripples,
          {
            top: top - diameter / 2,
            left: left - diameter / 2,
            height: Math.max(width, height),
            width: Math.max(width, height),
          },
        ]);
      };

      //add an event listener to the button
      elem.addEventListener("click", clickHandler);

      //clean up when the component is unmounted
      return () => {
        elem.removeEventListener("click", clickHandler);
      };
    }
  }, [ref, ripples]);

  //add a debounce so that if the user doesn't click after 1s, we remove the ripples
  const _debounced = useDebounce(ripples, 1000);
  useEffect(() => {
    if (_debounced.length) {
      setRipples([]);
    }
  }, [_debounced.length]);

  //map through the ripples and return span elements.
  //this will be added to the button component later
  return ripples?.map((style, i) => {
    return (
      <span
        key={i}
        style={{
          ...style,
          //should be absolutely positioned
          position: "absolute",
          backgroundColor: "#FFFFFF",
          opacity: "25%",
          transform: "scale(0)",
          // add ripple animation from styles.css
          animation: "ripple 600ms linear",
          borderRadius: "50%",
        }}
      />
    );
  });
};

export default useRipple;
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
Sponsored

Step 4: Use the useRipple hook on our button component#

Here's how to use it in our button component:

import React, { useRef } from "react";
import useRipple from "./useRipple";

const Button = () => {
  //create a ref to reference the button
  const ref = useRef<HTMLButtonElement>(null);
  //pass the ref to the useRipple hook
  const ripples = useRipple(ref);
  return (
    <button
      ref={ref}
      style={{
        padding: 16,
        backgroundColor: "#1e293b",
        color: "white",
        border: "none",
        borderRadius: 8,
        cursor: "pointer",
        position: "relative",
        overflow: "hidden",
      }}
    >
      {ripples}
      Button
    </button>
  );
};

export default Button;
1234567891011121314151617181920212223242526272829

With this we now have ripple effect within our buttons.

Sponsored

Conclusion#

We have successfully implemented a ripple effect using useRipple hook and we can use it in any element, and integrate it with any css library.

Hopefully you had fun during this tutorial! If you like tutorials like this in react, please subscribe to my newsletter down below to keep updated for future posts!

Credits: Image by Joe from Pixabay

Share this post!

Related Posts

Sponsored

Subscribe to our newsletter

Get up-to-date on latest articles on react.js, node.js, graphql, full-stack web development. No Spam. Helpful articles to improve your skills. Sent directly to your inbox.