@cariosan/react
Drop-in React components and hooks for Cariosan chat.
Pre-built React components + hooks layered on top of @cariosan/client. Use the components if you want chat UI in ten minutes; use the hooks if you want your own UI but the data layer done for you.
Provider
Wrap your app once:
The provider stores the Cariosan instance in context. Components and hooks read it via useCariosan().
Drop-in components
Accessibility built in — every component is keyboard-navigable, includes aria-live regions for new messages, and renders placeholders during SSR so they hydrate live on the client without layout shift.
Hooks
Use these when you want custom UI but don't want to re-implement subscription plumbing:
Every hook handles subscribe / unsubscribe on the underlying Channel automatically — no cleanup code in your components.
useMessages keeps its list live for reactions, edits, and unsends too: each Message exposes reactions ([{ emoji, count, reacted_by_me }]), edited_at, and deleted_at, updated in place as events arrive. useMessageActions(channelId) returns { addReaction, removeReaction, editMessage, deleteMessage } bound to the channel, and the built-in MessageList already renders reaction pills, an “(edited)” marker, and deleted-message tombstones.
Quoted replies
Send a reply by passing parentId to useMessageSender's sendMessage:
Each reply Message carries parent_id and a quoted_message preview ({ id, user_id, user_name, content, type, deleted }). The built-in MessageList renders that quote above the reply automatically — and shows "Original message was deleted" when the quoted parent has been unsent. See Quoted replies.
For the built-in compose flow (a "Reply" button on each bubble → a quote chip above the input → send), wrap the room in ReplyDraftProvider:
With the provider mounted, MessageList shows a Reply action per message, MessageInput shows a cancellable quote chip (Esc clears it), the next send is wired with parentId, and tapping a quote scrolls to the original. Without the provider these affordances stay hidden — the quote rendering on received replies always works regardless. Need finer control? Drive it yourself with useReplyDraft() ({ replyTo, setReplyTo, clearReply }).
Mentions
MessageInput ships an @ autocomplete over channel members. By default it lazily loads the roster via channel.getPresence() the first time you type @; pass mentionMembers to supply the candidate list yourself and skip the round-trip:
Picking a member inserts @Name and sends the resolved external_id in mentions — no fragile text parsing. MessageList highlights mention tokens automatically. For badges, useMentions() tracks live unread-mention counts per channel from the SDK's mention event, and the built-in ChannelList shows a mention dot:
useMentions accumulates from mount; seed accurate cold-load state from mention_unread_count on GET /v1/me/channels if you need it to survive reloads. See Mentions.
Read receipts
Pass currentUserId to MessageList and your own bubbles get a ✓ / ✓✓ / ✓✓-blue indicator that updates live as members deliver/read:
For custom bubbles, useReadReceipts(channelId) seeds the cursors and returns a receiptState(message) that re-renders on every cursor advance:
Delivery is acked automatically by the SDK. If a workspace disables read receipts, ticks never reach blue (delivery still shows). See Read & delivery receipts.
Search
useSearch(channelId?) powers a search box — pass a channelId to scope to one channel, omit it to search across all your channels. The box's styling is yours to provide.
See Search.
Theming
Note
All component styles are scoped — no global CSS conflicts.
No Tailwind or CSS-in-JS. Override CSS variables on any ancestor:
Full variable list:
Dark mode ships out of the box — set data-theme="dark" on any ancestor and every component picks it up. No extra config needed.
Bundle size
Measured against the current dist/ build (pnpm build && gzip -c dist/index.js | wc -c):
@cariosan/react: 2.6 KB gzipped (6.8 KB raw)@cariosan/react/styles.css: 1.2 KB gzipped (4.2 KB raw)@cariosan/client(peer): 3.0 KB gzipped (8.6 KB raw)
React + React DOM are peer dependencies, so they don't count toward the SDK weight. A complete React + Cariosan client + UI bundle lands around 51 KB gzipped including React 18.
Missing a component?
The hooks cover every data surface the components use. If your design requires something we haven't built (a sidebar, a search bar, channel settings), build it with the hooks — no need to eject.
Was this page helpful?