shadcn.io is not affiliated with official shadcn/ui
Pressing the button turns it on: the icon fills in its active color and pops, and a ring and six dots burst out around it. Pressing it again turns it off and empties the icon. The state is exposed as aria-pressed, so it's a real toggle.
Use it for like, favorite, bookmark, star or follow actions that flip one thing on and off. It keeps its own state, or you control it with active and onActiveChange to store the result. For a button that confirms a copy rather than a choice, use the Copy Button. It wraps the Button, as a round ghost icon button by default, so variant, pill and the icon sizes work. The fill is a CSS transition, and the pop and the burst are CSS keyframes, which the install adds to your stylesheet; a press that turns it on swaps them between two names, which restarts them, with no re-mount. With reduced motion there's no burst and no pop, and the icon still fills.
Installation
Usage
import { HeartIcon } from "lucide-react";
import { IconButton } from "@/components/ui/icon-button";<IconButton aria-label="Like" icon={<HeartIcon aria-hidden="true" />} />Demos
Controlled
Pass active and onActiveChange to keep the state yourself, for example to save it or to update a count. onActiveChange gets the new state on each press. Here the like count goes up and down with the heart.
Color
activeColor takes any CSS color and tints the filled icon and the burst; the default is var(--primary). Theme variables such as var(--destructive) or var(--foreground) follow light and dark mode. Hover and focus tint the empty icon in the same color, as a hint of what a press does.
Without the Burst
animate={false} keeps the fill and drops the pop, the ring and the dots, for a dense list or a toolbar where many toggles sit side by side.
Accessibility
The button has no text, so aria-label is required: it says what the button does, such as Like or Bookmark. It's announced as a toggle button, with aria-pressed saying whether it's on. The icon and the burst are hidden from assistive tech. Enter and Space toggle it, and focus tints the icon as hover does.
- Enter
- Turn the button on or off.
- Space
- Turn the button on or off.
FAQ
What can I pass as the icon?
Any icon element, such as a Lucide icon: icon={<HeartIcon aria-hidden="true" />}. Its outline is filled in activeColor when the button is on, so outline icons work best.
How do I save the like when it changes?
Control it: pass active and update it from onActiveChange, which gets the new state. Without active the button keeps its own state, starting from defaultActive. The Controlled demo keeps a count.
How do I make the heart red?
Pass activeColor="var(--destructive)". Any CSS color works, but a theme variable follows light and dark mode. The Color demo shows three.
Can I turn off the burst?
Yes, with animate={false}. The icon still fills and empties, as in the Without the Burst demo.
Does the burst play when the page loads with the button on?
No. It plays only when a press turns the button on, so a list of items people already liked loads quietly.
Why is aria-label required?
An icon has no name a screen reader can read, so without a label the button would be announced as just a button. The prop's type makes it required.
API Reference
IconButton
data-slot="icon-button-icon"
active?boolean
Whether the button is on (pressed), to hold that state yourself.
defaultActive?boolean
Whether the button starts on, when it keeps its own state.
- Default
false
onActiveChange?(active: boolean) → void
Called with the new state each time the button is pressed.
- Parameters
active– boolean- Returns
void
iconReactNode
The icon, such as <HeartIcon /> from lucide-react: its outline
fills in activeColor when on.
activeColor?string
The color of the filled icon and its burst, any CSS color.
- Default
"var(--primary)"
animate?boolean
Play the burst (a ring and a spray of dots) and the pop when a press turns the button on. Without it the icon still fills.
- Default
true
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
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.
data-state?string
"on" when on is true, otherwise "off".
--icon-button-colorstring
The filled icon's and the burst's color.
Related
- Pro
Counter Button
ControlsShow the count beside the toggle
- Pro
Confetti Button
Motion & feedbackA bigger celebration on press
- Pro
Theme Toggle Button
ControlsAnother icon-only toggle
Copy Button
Progress & resultsIcon swap that confirms an action
- Pro
GitHub Stars Button
Hover revealsStars, but from GitHub
Ripple Button
Motion & feedbackPress feedback from the touch point