Skip to content

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(*).

Reacting to someone’s activity creates a notification for its author and fires a reaction.added webhook.

// client.reactions.add(kind, activityId, custom?) -> Reaction
const reaction = await client.reactions.add('like', activity.id)

Seeding is live, not one-shot: when the activity you pass in comes back with different counts — someone else liked it and live: true revalidated the page — the button adopts them. A seed arriving while your own optimistic write is in flight is skipped, since it was built before your click and adopting it would visibly undo it; if that write then fails, the rollback lands on the skipped seed rather than on your pre-click state, so someone else’s like isn’t lost with yours. Render straight from counts; you never need to remount the button to see another user’s reaction.

The counter is still last-write-wins against the server. A refresh() issued before a click can land after it and briefly show pre-write counts; the next revalidation corrects it. Treat the number as eventually right, not transactional.

There is no server-side reactions.add — a reaction is always attributed to the authenticated user making the request, and a server token has no identity, so reactions are created only through the client SDK. To react on a user’s behalf from a backend, mint a user token for them first.

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)

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 with

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 })

The list is keyset-paginated like every other list in the API.

  • Notifications — where a reaction shows up for the author.
  • Activities — the objects being reacted to, and how deleting one affects its reactions.
  • Webhooks — reaction.added / reaction.removed delivered to your backend.