Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Buttons

Free

Ripple Button for React

A button that sends a ripple out from the exact point it was pressed, as feedback that the press landed. For React with Base UI, when a tap should feel answered.

Framework
UI library

Each press starts a circle where the finger or pointer landed, and it spreads to cover the button as it fades. A keyboard press starts it from the center.

Use it where people should feel their press was taken, on a primary action, a toolbar, or a touch screen where there's no hover to lean on. It wraps the Button, so variant and size work as usual. The ripple is a CSS keyframe animation, which the install adds to your stylesheet; a press only writes where it landed into two CSS variables and restarts the animation, so nothing re-renders. It's left out entirely when the system asks for reduced motion.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Use email insteadNo credit card

Usage

import { RippleButton } from "@/components/ui/ripple-button";
<RippleButton>Save changes</RippleButton>

Demos

Color

rippleColor is the ripple's color, any CSS color, drawn at about a third of its strength. By default it's the label's color, which reads on every variant. Here an outline button ripples in the primary color and a ghost button in the destructive one, so the feedback matches what the action does.

Preview width

Duration

duration is how long one ripple takes to spread and fade, in milliseconds, and the default is 600. A short one suits small, frequent actions; a long one gives a large button a slower, softer wave. Ripples from quick repeated presses overlap rather than restart.

Preview width

Accessibility

The ripple is decoration and hidden from assistive tech: the button is announced and activated as any Button. It starts from the center on Enter and Space, so keyboard presses get the same feedback.

Enter
Activate the button.
Space
Activate the button.

FAQ

Where does the ripple start?

Where the press landed, measured against the button's own box, so it follows the finger on a wide button, for a mouse, a touch or a pen alike. Enter and Space have no point, so they start it from the center.

How do I change the ripple's color?

Pass rippleColor, any CSS color or a theme variable such as var(--primary). The default is the label's color. The Color demo uses the primary and destructive colors.

How do I make the ripple slower or faster?

Set duration in milliseconds; 600 is the default. The Duration demo shows 350 and 1200.

What happens when someone presses several times quickly?

Each press starts the ripple again from the new point, so a quick double press shows two short ripples rather than a pile of them. There's only ever one ripple element, however often it's pressed.

Do I need to add keyframes to my global CSS?

The install does it: the CLI adds the ripple-button keyframes and their animate-ripple-button utilities to your stylesheet. Copying the file by hand, add the CSS shown under Manual in Installation.

What happens with reduced motion?

No ripple is drawn. The button still presses, with the usual small scale feedback.

API Reference

RippleButton

data-slot="ripple-button-ripple"

PropType
children?ReactNode

The label; the ripple spreads under it.

duration?number

How long one ripple takes to spread and fade, in milliseconds.

Default
600
focusableWhenDisabled?boolean

Whether the button should be focusable when disabled.

Default
false
nativeButton?boolean

Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).

Default
true
rippleColor?string

The ripple's color, any CSS color, at a third of its strength. By default the label's color.

render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

CSS variableType
--ripple-button-colorstring

The ripple's color (the label's by default).

--ripple-button-durationstring

One ripple's spread and fade (600ms).

--ripple-button-xstring

Where the last press landed, from the left.

--ripple-button-ystring

Where the last press landed, from the top.

All 35 buttons