Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Buttons

Pro

Pulse Button for React

A button with two rings that pulse out from its edge, to draw the eye to the one action that matters. For React with Base UI, for an onboarding step or an upgrade.

Framework
UI library

Two thin rings leave the button's edge and fade as they spread, one after the other, in a loop. They take the button's corners at every size, pill included.

Use it for the single action you most want taken on a screen: start a trial, take the tour, finish setup. Keep it to one per page, and turn it off once it has done its job; a pulse that never stops turns into noise. For a glow that answers the pointer instead, use the Neon Button. It wraps the Button, so variant and size work as usual. The rings are CSS keyframes the install adds to your stylesheet, with no animation library. When the system asks for reduced motion they stop and one ring stays, still and faint.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { PulseButton } from "@/components/ui/pulse-button";
<PulseButton>Start free trial</PulseButton>

Demos

Stop After a Click

pulse turns the rings on and off. Here they stop once the button has been pressed, since it has been noticed by then. Driving it from state works the same way after a timeout, or once the section has scrolled out of view.

Preview width

Color

The rings follow the variant: the primary color by default and the destructive color on a destructive button. pulseColor takes any CSS color for another one, here a quiet var(--muted-foreground) ring on an outline button.

Preview width

Duration

duration is one ring's spread and fade in milliseconds; the default is 2000, and the second ring always runs half a cycle behind the first. A fast pulse, 1200 here, feels urgent; a slow one, 3200, is a gentle reminder.

Preview width

Accessibility

The rings are decoration and hidden from assistive tech: the button is announced and activated as any Button. They hide while the button is disabled, so a pulse never points at something that can't be pressed.

Enter
Activate the button.
Space
Activate the button.

FAQ

How do I stop the pulse once it has been seen?

Set pulse={false}. Keep it in state and turn it off after the first click, a few seconds in, or when the user scrolls past, as in the Stop After a Click demo.

How do I change the rings' color?

Pass pulseColor, any CSS color such as var(--muted-foreground) or a hex code. Without it the rings are the primary color, or the destructive color on the destructive variant.

Can I make it pulse faster or slower?

Set duration in milliseconds; 2000 is the default. The second ring always runs half a cycle behind the first, as the Duration demo shows.

Do the rings follow rounded or pill buttons?

Yes. Each ring is a line on the button's own edge, with its corners, that grows outward, so it keeps the button's shape, pill included, at every size. It grows a little more up and down than across, so a wide button gets an even margin all round.

Will a pulse that loops forever slow the page down?

No. The keyframes only change each ring's scale and opacity, which the browser can hand to the compositor: no repaint, no re-render and no JavaScript while it runs.

Why are the rings cut off?

They spread a few pixels past the button. A parent with overflow: hidden and no room around the button cuts them; give it some padding.

Is it too distracting for a dashboard?

It can be. Use one at a time, on the action that matters most, and stop it once it has been seen. For a smaller hint, use a muted pulseColor or a longer duration.

What happens with reduced motion?

The rings stop moving and one stays, a little way out and faint, so the button still stands out without anything moving.

API Reference

PulseButton

data-slot="pulse-button-rings"

PropType
children?ReactNode

The label; the rings pulse out around it.

duration?number

How long one ring takes to spread and fade, in milliseconds. The second ring is always half a cycle behind the first.

Default
2000
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
pulse?boolean

Run the rings. Turn it off once the button has been noticed: after the first click, or a few seconds in.

Default
true
pulseColor?string

The rings' color, any CSS color. By default the primary color, or the destructive color on the destructive variant.

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
--pulse-button-colorstring

The rings' color.

--pulse-button-durationstring

One ring's spread and fade (2s).

--pulse-button-scalestring

How far a ring grows, across and down (1.1 1.35; 1.35 on an icon button).

All 35 buttons