Reactions
Users react to activities. Counts are denormalized (reaction_counts) and bumped only
when a write actually affected a row — the read path never runs COUNT(*).
Add a reaction
Section titled “Add a reaction”// client.reactions.add(kind, activityId, custom?) -> Reactionconst reaction = await client.reactions.add('like', activity.id)import { useReactions } from '@dropin/react'
function LikeButton({ activity }: { activity: { id: string; reaction_counts: Record<string, number>; own_reactions?: string[] } }) { // Seed from the activity you already fetched — this hook manages optimistic // like/unlike state, it does not fetch counts itself. const { react, unreact, counts, ownReactions } = useReactions( activity.id, activity.reaction_counts, activity.own_reactions ?? [], ) const liked = ownReactions.includes('like') return ( <button onClick={() => (liked ? unreact('like') : react('like'))}> {liked ? '❤️' : '🤍'} {counts.like ?? 0} </button> )}There is no server-side reactions.add — reactions are always attributed to the
authenticated user making the request, so they’re created only through the client SDK.
Remove a reaction
Section titled “Remove a reaction”Two ways to remove: unreact drops the caller’s own reaction of a given kind (no
reaction id needed); delete removes a specific reaction by its id.
// Remove your own reaction of a kind:await client.reactions.unreact(activity.id, 'like')
// Or remove a specific reaction by id (e.g. one returned from reactions.add/list):await client.reactions.delete(reaction.id)const { unreact } = useReactions(activity.id, activity.reaction_counts, activity.own_reactions ?? [])await unreact('like')// Admin-only: deletes any user's reaction by id, bypassing ownership checks.await server.reactions.delete(reaction.id)Read counts
Section titled “Read counts”Reaction counts and the caller’s own reactions arrive denormalized on every Activity
returned from a feed read — no separate request needed:
activity.reaction_counts // Record<string, number>, e.g. { like: 4 }activity.own_reactions // string[] | undefined — kinds the caller has already reacted withList who reacted
Section titled “List who reacted”For the full “who reacted” list (rather than just counts), use client.reactions.list
or the paginated useReactionList hook:
// Page<Reaction>, newest first. `kind` optionally filters server-side.const page = await client.reactions.list(activity.id, { kind: 'like', limit: 20 })import { useReactionList } from '@dropin/react'
const { reactions, loadNext, hasNext, isLoading, remove } = useReactionList(activity.id, { kind: 'like' })// remove(reactionId) optimistically drops it from the list, then calls reactions.delete