Reaction bar
FreeEmoji reactions under a message or post. Each chip is a toggle; the count rolls and your chip settles with one small pop. The picker adds a new reaction.
pnpm dlx shadcn@latest add https://caliperui.site/r/reaction-bar.jsonMaya Okafor shipped invoice reminders to every workspace.
When to use #
Use it for
- Comments, chat messages, changelog entries.
- Light feedback where a reply would be too much.
Skip it when
- Votes that rank items; use the vote control.
- Ratings with a scale; use the rating.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/reaction-bar.json
Adds its 4 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above. It is built from toggleReaction and ReactionBar.
import { Rocket } from "lucide-react" import { useState } from "react" import { ReactionBar, type Reaction } from "@/components/ui/reaction-bar" export function ReactionBarDemo() { const [reactions, setReactions] = useState<Reaction[]>([ { emoji: "🎉", label: "Party", count: 12, mine: true }, { emoji: "👀", label: "Eyes", count: 4 }, { emoji: "🚀", label: "Rocket", count: 1280 }, ]); return ( <div className="grid w-full max-w-md gap-3 rounded-surface border border-border bg-surface p-4"> <p className="text-sm"><span className="font-medium">Maya Okafor</span> <span className="text-muted-foreground">shipped</span> invoice reminders to every workspace.</p> <ReactionBar reactions={reactions} onChange={setReactions} /> </div> ); }
API reference #
toggleReaction
component
No props of its own; it accepts the native props of its root element.
ReactionBar
component
Accessibility #
It exposes group semantics, with aria-label, aria-pressed and aria-hidden kept in step with state.
Keyboard and focus handling comes from Radix popover: arrow keys move through items, Enter and Space select, and Escape closes and returns focus.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.