shadcn.io is an independent project built on shadcn/ui
Buttons
FreeIcon Button for Solid
An icon toggle that fills and bursts with particles when it turns on, for likes and bookmarks. For Solid with Ark UI, when a toggle needs feedback.
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-solid";
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
iconJSX.Element
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
clickEffect?boolean
Apply a click effect to the button.
- Default
true
isLoading?boolean
Show a loading indicator.
- Default
false
asChild?(props: ParentProps) => JSX.Element
Render your own element: a function that gets the part's props to spread on it.
- Type
(props: ParentProps) => JSX.Element
class?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.





