Message Reactions
Stacked emoji counts with reacted styling and a quick-add menu.
Interactive
Toggle reactions and pick new emoji from the menu.
default-example.tsx
1"use client";23import { useState } from "react";4import { MessageReactions } from "@/registry/ui/message-reactions";5import type { Reaction } from "@/registry/ui/chat-types";67const INITIAL: Reaction[] = [8 { emoji: "👍", count: 4, reacted: false },9 { emoji: "❤️", count: 2, reacted: true },10];1112export function MessageReactionsDefaultExample() {13 const [reactions, setReactions] = useState<Reaction[]>(INITIAL);1415 const onReact = (emoji: string) => {16 setReactions((prev) => {17 const idx = prev.findIndex((r) => r.emoji === emoji);18 if (idx === -1) {19 return [...prev, { emoji, count: 1, reacted: true }];20 }21 const row = prev[idx];22 if (row.reacted) return prev;23 const next = [...prev];24 next[idx] = { ...row, count: row.count + 1, reacted: true };25 return next;26 });27 };2829 const onRemoveReaction = (emoji: string) => {30 setReactions((prev) => {31 const idx = prev.findIndex((r) => r.emoji === emoji);32 if (idx === -1) return prev;33 const row = prev[idx];34 if (!row.reacted) return prev;35 const nextCount = row.count - 1;36 if (nextCount <= 0) {37 return prev.filter((_, i) => i !== idx);38 }39 const next = [...prev];40 next[idx] = { ...row, count: nextCount, reacted: false };41 return next;42 });43 };4445 return (46 <div className="flex justify-center w-full max-w-lg p-4">47 <MessageReactions48 reactions={reactions}49 onReact={onReact}50 onRemoveReaction={onRemoveReaction}51 />52 </div>53 );54}
Installation & source
Install via the shadcn CLI or copy the registry files manually.
bash
npx shadcn@latest add @tt-ui/message-reactions