Search

Search the site

All components

Message Reactions

Stacked emoji counts with reacted styling and a quick-add menu.

Interactive
Toggle reactions and pick new emoji from the menu.
1"use client";
2
3import { useState } from "react";
4import { MessageReactions } from "@/registry/ui/message-reactions";
5import type { Reaction } from "@/registry/ui/chat-types";
6
7const INITIAL: Reaction[] = [
8 { emoji: "👍", count: 4, reacted: false },
9 { emoji: "❤️", count: 2, reacted: true },
10];
11
12export function MessageReactionsDefaultExample() {
13 const [reactions, setReactions] = useState<Reaction[]>(INITIAL);
14
15 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 };
28
29 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 };
44
45 return (
46 <div className="flex justify-center w-full max-w-lg p-4">
47 <MessageReactions
48 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