learn react ui logoLearnReactUI
Ripple Effect Button

React Ripple Effect Tutorial with SCSS

In this tutorial, we’ll create a reusable ripple effect component in React using SCSS. This effect is commonly used in CSS Animation to provide feedback when a user interacts with a button.

Demo link is here

Prerequisites

  • Basic understanding of React, JavaScript, and SCSS.
  • A code editor (e.g., VSCode, Sublime Text).
  • Node.js installed on your machine.

1. Setup the React Component

We’ll start by creating two components: Button and RippleButton. The RippleButton component will handle the ripple effect, while Button will serve as a simple wrapper that can be extended.

Create the RippleButton.js File

import React from "react";
import classnames from "classnames";
import Styles from "./RippleButton.module.scss";

export const Button = ({ className, children, onClick }) => {
  return (
    <div className={className} onClick={onClick}>
      {children}
    </div>
  );
};

export const RippleButton = ({ className, children, onClick, ...rest }) => {
  const mounted = React.useRef(false);
  const [isRippling, setIsRippling] = React.useState(false);
  const [coords, setCoords] = React.useState({ x: -1, y: -1 });

  React.useEffect(() => {
    mounted.current = true;
    return () => {
      mounted.current = false;
    };
  }, []);

  React.useEffect(() => {
    if (mounted) {
      if (coords.x !== -1 && coords.y !== -1) {
        setIsRippling(true);
        setTimeout(() => setIsRippling(false), 500);
      } else {
        setIsRippling(false);
      }
    }
  }, [coords, mounted]);

  React.useEffect(() => {
    if (mounted) {
      if (!isRippling) setCoords({ x: -1, y: -1 });
    }
  }, [isRippling]);

  return (
    <Button
      className={classnames(Styles["waves-effect"], {
        [className]: className,
      })}
      onClick={(e) => {
        const rect = e.target.getBoundingClientRect();
        setCoords({ x: e.clientX - rect.left, y: e.clientY - rect.top });
        if (onClick) {
          onClick(e);
        }
      }}
      {...rest}
    >
      {children}
      {isRippling ? (
        <span
          className={Styles["waves-ripple"]}
          style={{
            left: coords.x,
            top: coords.y,
          }}
        ></span>
      ) : null}
    </Button>
  );
};

Button.Ripple = RippleButton;

Explanation

Button Component

  • A simple wrapper that handles the click event and displays children.

RippleButton Component

  • Handles the ripple effect. When clicked, it calculates the coordinates of the click relative to the button and sets these coordinates to create a ripple at the click position.

useRef and useEffect Hooks

  • Used to manage the ripple state and ensure that the effect is only active while the component is mounted.

2. Create the SCSS for the Ripple Effect

Next, we’ll create the SCSS file that defines the styles for the ripple effect.

Create the RippleButton.module.scss File

.waves-effect {
  position: relative;
  overflow: hidden;

  .waves-ripple {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    opacity: 0;
    width: 0;
    height: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.2);
    animation: ripple-effect 0.7s ease;
  }
}

@keyframes ripple-effect {
  0% {
    opacity: 0;
  }
  25% {
    opacity: 1;
  }
  100% {
    width: 200%;
    padding-bottom: 200%;
    opacity: 0;
  }

Explanation

  • .waves-effect: This class is applied to the button container to handle the positioning and overflow of the ripple effect.

  • .waves-ripple: The ripple element starts small and expands while fading out. The transform: translate(-50%, -50%); ensures that the ripple is centered at the click position.

  • @keyframes ripple-effect: Defines the animation for the ripple effect, where the ripple grows in size and fades out as it expands.

Example Usage in App.js

import React from "react";
import { RippleButton } from "./RippleButton";

function App() {
  return (
    <div className="App">
      <RippleButton className="my-button">Click Me</RippleButton>
    </div>
  );
}

export default App;