shadcn.io is not affiliated with official shadcn/ui
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.
Installation
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.
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.
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.
API Reference
RippleButton
data-slot="ripple-button-ripple"
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.
--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.
Related
- Pro
Confetti Button
Motion & feedbackA burst of confetti on press
- Pro
Depth Button
Motion & feedbackA raised button that presses in
- Pro
Pulse Button
Motion & feedbackRings that call for attention
- Pro
Spotlight Button
Glow & lightA glow that follows the cursor
- Pro
Shake Button
Motion & feedbackA shake when something's wrong
- Pro
Success Button
Progress & resultsLoading, then a check when done