React AriaExamples

Ripple Button

An animated Ripple Button styled with Tailwind CSS.

Example#


import {Button} from 'react-aria-components';
import {useEffect, useRef, useState} from 'react';
import Airplane from '@spectrum-icons/workflow/Airplane';

function RippleButton(props) {
  const [coords, setCoords] = useState({ x: -1, y: -1 });
  const [isRippling, setIsRippling] = useState(false);

  let timeout = useRef<ReturnType<typeof setTimeout> | null>(null);
  let onPress = (e) => {
    setCoords({ x: e.x, y: e.y });
    if (e.x !== -1 && e.y !== -1) {
      setIsRippling(true);
      timeout.current = setTimeout(() => setIsRippling(false), 600);
    }
  };
  useEffect(() => {
    return () => {
      clearTimeout(timeout.current);
    };
  }, []);

  return (
    <div className="bg-gradient-to-r from-teal-300 to-cyan-500 p-12 rounded-lg flex justify-center">
      <Button
        onPress={onPress}
        className={`
      relative overflow-hidden min-h-16
      inline-flex items-center justify-center rounded-md bg-black bg-opacity-50 bg-clip-padding border border-white/20 px-3.5 py-2 font-medium text-white
      hover:bg-opacity-60 pressed:bg-opacity-70 transition-colors cursor-default outline-none focus-visible:ring-2 focus-visible:ring-white/75`}
      >
        {isRippling
          ? (
            <div
              className="ripple"
              style={{
                left: coords.x - 15,
                top: coords.y - 15
              }}
            />
          )
          : (
            ''
          )}
        <span className="flex items-center gap-2">{props.children}</span>
      </Button>
    </div>
  );
}

<RippleButton>
  <Airplane size="S" /> Book flight
</RippleButton>
import {Button} from 'react-aria-components';
import {useEffect, useRef, useState} from 'react';
import Airplane from '@spectrum-icons/workflow/Airplane';

function RippleButton(props) {
  const [coords, setCoords] = useState({ x: -1, y: -1 });
  const [isRippling, setIsRippling] = useState(false);

  let timeout = useRef<
    ReturnType<typeof setTimeout> | null
  >(null);
  let onPress = (e) => {
    setCoords({ x: e.x, y: e.y });
    if (e.x !== -1 && e.y !== -1) {
      setIsRippling(true);
      timeout.current = setTimeout(
        () => setIsRippling(false),
        600
      );
    }
  };
  useEffect(() => {
    return () => {
      clearTimeout(timeout.current);
    };
  }, []);

  return (
    <div className="bg-gradient-to-r from-teal-300 to-cyan-500 p-12 rounded-lg flex justify-center">
      <Button
        onPress={onPress}
        className={`
      relative overflow-hidden min-h-16
      inline-flex items-center justify-center rounded-md bg-black bg-opacity-50 bg-clip-padding border border-white/20 px-3.5 py-2 font-medium text-white
      hover:bg-opacity-60 pressed:bg-opacity-70 transition-colors cursor-default outline-none focus-visible:ring-2 focus-visible:ring-white/75`}
      >
        {isRippling
          ? (
            <div
              className="ripple"
              style={{
                left: coords.x - 15,
                top: coords.y - 15
              }}
            />
          )
          : (
            ''
          )}
        <span className="flex items-center gap-2">
          {props.children}
        </span>
      </Button>
    </div>
  );
}

<RippleButton>
  <Airplane size="S" /> Book flight
</RippleButton>
import {Button} from 'react-aria-components';
import {
  useEffect,
  useRef,
  useState
} from 'react';
import Airplane from '@spectrum-icons/workflow/Airplane';

function RippleButton(
  props
) {
  const [
    coords,
    setCoords
  ] = useState({
    x: -1,
    y: -1
  });
  const [
    isRippling,
    setIsRippling
  ] = useState(false);

  let timeout = useRef<
    ReturnType<
      typeof setTimeout
    > | null
  >(null);
  let onPress = (e) => {
    setCoords({
      x: e.x,
      y: e.y
    });
    if (
      e.x !== -1 &&
      e.y !== -1
    ) {
      setIsRippling(
        true
      );
      timeout.current =
        setTimeout(
          () =>
            setIsRippling(
              false
            ),
          600
        );
    }
  };
  useEffect(() => {
    return () => {
      clearTimeout(
        timeout.current
      );
    };
  }, []);

  return (
    <div className="bg-gradient-to-r from-teal-300 to-cyan-500 p-12 rounded-lg flex justify-center">
      <Button
        onPress={onPress}
        className={`
      relative overflow-hidden min-h-16
      inline-flex items-center justify-center rounded-md bg-black bg-opacity-50 bg-clip-padding border border-white/20 px-3.5 py-2 font-medium text-white
      hover:bg-opacity-60 pressed:bg-opacity-70 transition-colors cursor-default outline-none focus-visible:ring-2 focus-visible:ring-white/75`}
      >
        {isRippling
          ? (
            <div
              className="ripple"
              style={{
                left:
                  coords
                    .x -
                  15,
                top:
                  coords
                    .y -
                  15
              }}
            />
          )
          : (
            ''
          )}
        <span className="flex items-center gap-2">
          {props
            .children}
        </span>
      </Button>
    </div>
  );
}

<RippleButton>
  <Airplane size="S" />
  {' '}
  Book flight
</RippleButton>
div.ripple {
  position: absolute;
  height: 30px;
  width: 30px;
  border-radius: 50%;
  transform: scale(0);
  opacity: 1;
  animation: ripple 600ms linear;
  background-color: rgba(255, 255, 255, .6);
}
@keyframes ripple {
  from {
    transform: scale(0);
    opacity: 1;
  }
  to {
    transform: scale(6);
    opacity: 0;
  }
}
div.ripple {
  position: absolute;
  height: 30px;
  width: 30px;
  border-radius: 50%;
  transform: scale(0);
  opacity: 1;
  animation: ripple 600ms linear;
  background-color: rgba(255, 255, 255, .6);
}
@keyframes ripple {
  from {
    transform: scale(0);
    opacity: 1;
  }
  to {
    transform: scale(6);
    opacity: 0;
  }
}
div.ripple {
  position: absolute;
  height: 30px;
  width: 30px;
  border-radius: 50%;
  transform: scale(0);
  opacity: 1;
  animation: ripple 600ms linear;
  background-color: rgba(255, 255, 255, .6);
}
@keyframes ripple {
  from {
    transform: scale(0);
    opacity: 1;
  }
  to {
    transform: scale(6);
    opacity: 0;
  }
}

Tailwind config#

This example uses the tailwindcss-react-aria-components plugin. Add it to your tailwind.config.js:

module.exports = {
  // ...
  plugins: [
    require('tailwindcss-react-aria-components')
  ]
};
module.exports = {
  // ...
  plugins: [
    require('tailwindcss-react-aria-components')
  ]
};
module.exports = {
  // ...
  plugins: [
    require(
      'tailwindcss-react-aria-components'
    )
  ]
};

Components#


Button
A button allows a user to perform an action, with mouse, touch, and keyboard interactions.