Chat
ReactionBar
A message's reactions: each one's emoji and how many chose it, with the person's own pressed and tinted, and quick reactions to add with a single press. Pressing a reaction calls onReact with its key, so the app adds the person's reaction or takes it back. Each reaction is a toggle button (aria-pressed) that screen readers hear by name and count ("Like, 3"). Without onReact the reactions are read-only, and with nothing to show it renders nothing.
Install
Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):
npx shadcn@latest init --base base --preset nova --yes --no-monoreponpx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):
npx shadcn@latest add ourostack/ouro-ui/reaction-bar#v1.2.13Or, with the @ouro namespace set up:
npx shadcn@latest add @ouro/ouro-base @ouro/reaction-barIts tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):
npx shadcn@latest add ourostack/ouro-ui/reaction-bar#v1.2.13 ourostack/ouro-ui/reaction-bar-tests#v1.2.13import { ReactionBar } from "@/components/ouro/reaction-bar/reaction-bar";Paths use shadcn's default @/ alias; the CLI rewrites them to yours.
Example
<ReactionBar reactions={message.reactions} quick={quickReactions} onReact={(key) => toggleReaction(message.id, key)} />Usage
Place it in Message's attachments.
Use instead when
- Use
ToggleGroupinstead for a choice between options, andBadgefor a single count.
Props
Type: ReactionBarProps.
className- Type:
string - Layout only: margins, placement (
mt-1). Appearance classes warn in development. disabled- Type:
boolean - Keeps the counts but turns off reacting, for example while the app is offline.
label- Type:
string - Default:
"Reactions" - What screen readers hear for the group, for other languages.
mineLabel- Type:
string - Default:
"including you" - What screen readers hear after a read-only reaction that is the person's own.
onReact- Type:
((key: string, event: MouseEvent<HTMLElement>) => void) - Called with a reaction's key when it is pressed, to add the person's reaction or take it back. The click event comes second, so the app can check
event.isTrustedbefore it acts. Without it the reactions are read-only. quick- Type:
readonly ReactionOption[] - Reactions offered for a single press, after the counts. One that is already in
reactionsshows there instead. Shown only withonReact. reactions(required)- Type:
readonly Reaction[] - The message's reactions, in the order they show.
ref- Type:
Ref<HTMLDivElement> - Reaches the rendered element.
Native attributes
Also accepts the attributes in React's HTMLAttributes (id, aria-*, event handlers), passed to the rendered element, except those named on this page.
Don't use
These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.
items: Not a prop. The reactions arereactions:<ReactionBar reactions={message.reactions}>.onReactionClick: Not a prop. A pressed reaction callsonReact:<ReactionBar onReact={(key) => react(key)}>.
className is for layout only
Pass margins, width and grid placement (mt-4, w-full, col-span-2). Your classes always win over Ouro UI's: ReactionBar merges className last with cn (tailwind-merge), so a class that conflicts with its own replaces it.layout-classes
Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.
Alignment classes (text-center) warn too: set alignment on a parent element.
Types
Reaction
A reaction on a message, with how many people chose it.
interface Reaction extends ReactionOption {
/** How many people reacted this way. */
count: number;
/** The person using the app is one of them: the reaction is pressed and tinted. */
mine?: boolean | undefined;
}ReactionOption
A reaction someone can add: its key, its spoken name and what it shows.
interface ReactionOption {
/** Identifies the reaction ("like"); `onReact` receives it. */
key: string;
/** What screen readers hear for it ("Like"). */
label: string;
/** What it shows: an emoji ("๐") or an icon element. Screen readers hear `label` instead. */
emoji: ReactNode;
}Claim sources
- layout-classes: test:registry/ouro/components/ouro/reaction-bar/reaction-bar.test.tsx::ReactionBar > forwards a ref and lets app layout classes win
verify:testExists(registry/ouro/components/ouro/reaction-bar/reaction-bar.test.tsx::ReactionBar > forwards a ref and lets app layout classes win)