FivFold - Logo

Chat Kit

Full-featured iMessage/WhatsApp-inspired chat with 1-1 and group conversations, attachments, polls, GIFs, location sharing, reactions, and Socket.IO real-time transport.

Quick reference

CLI commands

Bash
# Add Chat UI components npx @fivfold/ui add chat # Add Chat backend (NestJS + TypeORM + Socket.IO) npx @fivfold/api add chat --framework=nestjs --orm=typeorm # Use MongoDB + Mongoose npx @fivfold/api add chat --framework=nestjs --orm=mongoose # Use Express + Prisma npx @fivfold/api add chat --framework=express --orm=prisma # Dry run to preview generated files npx @fivfold/ui add chat --dry-run npx @fivfold/api add chat --dry-run

Overview

The Chat Kit is a production-ready messaging system scaffolded across your full stack. It follows Hexagonal Architecture so you can swap databases, ORMs, and real-time providers without touching your domain logic. Supported combinations:

LayerOptions
FrameworkNestJS, Express
SQLTypeORM (PostgreSQL, MySQL, MariaDB, MSSQL), Prisma
NoSQLMongoose (MongoDB), Prisma (MongoDB connector)
Cloud NoSQLAzure Cosmos DB SDK, AWS DynamoDB SDK
RealtimeSocket.IO (WebSocket — auto-selected)

1-1 & Group Chats

Private DMs and multi-participant group conversations with admin roles, member management, and leave/remove actions.

Rich Messages

Text, images, videos, files, audio, GIFs (Tenor API), location pins, and interactive polls — all sent as typed message variants.

Real-time Updates

Socket.IO WebSocket rooms with typing indicators, presence detection, read receipts, and message delivery events — works with any database.

Reactions & Read Receipts

Per-message emoji reactions, per-message read-by tracking, and animated typing indicators with user avatars.

Search

Full-text search across conversations and message content with paginated results grouped by type.

Glassmorphism UI

Backdrop-blur message bubbles, smooth framer-motion animations, and fully responsive layout from mobile to desktop.

Architecture

The Chat Kit backend strictly follows Hexagonal (Ports & Adapters) Architecture. The domain port (IChatService) is framework and database agnostic — the delivery and infrastructure layers are generated separately based on your stack selection.

Chat Architecture

Demo

Interactive preview with simulated real-time replies and mock data

Messages
SJ
Sarah Johnson2m
Sounds great! See you at 3pm 🎉
DT
Design Team1h
Alex: New mockups uploaded to Figma
MC
Marcus Chen3h
Did you see the new product launch?
EW
Emma WilsonYesterday
The report is ready for review
SJ
SJ
Hey! Are you free for a quick call today?
10:30 AM
Sure, what time works for you?
10:32 AM
SJ
How about 3pm?
10:33 AM
SJ
Sounds great! See you at 3pm 🎉
10:34 AM
Contact Info
SJ

Sarah Johnson

Online

Shared Media

Guide

Step-by-step guides for the frontend UI and backend API integration. Select your stack in the API tab to see stack-specific instructions.

The Chat Kit UI is built on shadcn/ui with Tailwind CSS v4. All components are fully customizable and support light/dark mode via CSS variables.

1Install the Chat Kit

Run the FivFold UI CLI after initializing your project with npx @fivfold/ui init.

Bash
npx @fivfold/ui add chat

Components are placed in @/components/ui/kits/chat/. Additional npm packages (framer-motion, date-fns, emoji-mart) are installed automatically.

2Generated file structure

File tree
kits/chat/ types.ts # All shared TypeScript types index.tsx # ChatKit root component + re-exports chat-avatar.tsx # ChatAvatar, GroupAvatar threads-list.tsx # Sidebar conversation list thread-item.tsx # Individual thread row with context menu conversation.tsx # Message view with infinite scroll message-bubble.tsx # Message bubble (text, image, poll, location, GIF) message-input.tsx # Compose bar with attachment/GIF/poll/location typing-indicator.tsx # Animated typing dots attachment-picker.tsx # File/image/video picker with drag-and-drop gif-picker.tsx # Tenor GIF search and picker poll-creator.tsx # Create poll dialog poll-display.tsx # Rendered poll with vote progress location-picker.tsx # Share location (geolocation or manual) location-display.tsx # Static map + Google Maps link reaction-picker.tsx # Quick emoji reaction overlay search-panel.tsx # Full-text search dialog new-chat-dialog.tsx # Start DM or create group dialog contact-detail.tsx # Contact info sheet group-detail.tsx # Group info, members, admin controls

3Import and use in your app

app/chat/page.tsx
import { ChatKit } from "@/components/ui/kits/chat"; import type { FivFoldChatConversation, FivFoldChatMessage, FivFoldChatUser, } from "@/components/ui/kits/chat"; export default function ChatPage() { const currentUser: FivFoldChatUser = { id: "u1", name: "You", email: "you@example.com", }; return ( <ChatKit currentUser={currentUser} conversations={conversations} onSendMessage={handleSendMessage} onCreateConversation={handleCreate} onVotePoll={handleVote} onAddReaction={handleReaction} onMarkRead={handleMarkRead} /> ); }

4ChatKit props reference

PropTypePurpose
currentUserFivFoldChatUserThe logged-in user. Used to determine sent/received and show own avatar.
conversationsFivFoldChatConversation[]All conversations to display in the sidebar thread list.
onSendMessage(convId, dto) => voidCalled when user sends any message type (text, attachment, poll, location, GIF).
onCreateConversation(dto) => Promise<Conversation>Called when user starts a new DM or group. Return the created conversation.
onVotePoll(pollId, optionIds) => voidCalled when user votes on a poll. PATCH /api/chat/polls/:id/vote.
onAddReaction(msgId, emoji) => voidCalled when user reacts to a message. POST /api/chat/messages/:id/reactions.
onMarkRead(msgId) => voidCalled when a message enters the viewport. PATCH /api/chat/messages/:id/read.
typingUsersRecord<string, string[]>Map of convId → userIds currently typing. Drives the animated typing indicator.
onTyping(convId, isTyping) => voidEmit typing start/stop events to your real-time backend.
loadingMessagesbooleanShow skeleton loaders while fetching message history.
hasMoreMessagesbooleanEnable the "load more" trigger at the top of the message list.
onLoadMore() => voidCalled when user scrolls to the top. Fetch the next page of messages.

shadcn dependencies

ComponentUsed in
buttonThroughout
inputSearch, compose, group name edit
dialogGIF picker, poll creator, attachment picker, search, new chat
sheetContact detail, group detail panels
scroll-areaThread list, message list, member list
context-menuThread item actions, message actions
badgeUnread count, admin role
separatorSection dividers
tabsNew chat dialog (DM / Group)
switchPoll allow-multiple-answers toggle
progressPoll vote percentage bars
alert-dialogLeave group confirmation
tooltipReaction counts, action hints

5Third-party integrations

The Chat Kit UI integrates with external services for GIFs, file uploads, and location sharing. Configure these in your frontend; the API supports the corresponding message types (see API tab).

Tenor GIF integration

Tenor (by Google) provides a GIF search API. The gif-picker.tsx component calls Tenor directly for search; when the user selects a GIF, it sends a message via onSendMessage with type: "gif" and metadata: { tenorId, url, previewUrl, ... }.

Flow: (1) User opens GIF picker → frontend calls Tenor Search API with query. (2) User selects GIF → call onSendMessage(convId, { type: "gif", metadata: { tenorId, url, previewUrl, description } }). (3) Your backend receives POST /api/chat/conversations/:id/messages and creates the message. (4) Optionally call Tenor's Register Share endpoint when a GIF is sent to improve future search results.

Tenor Search
// gif-picker.tsx — Tenor Search (frontend) const res = await fetch( `https://tenor.googleapis.com/v2/search?q=${query}&key=${TENOR_API_KEY}&client_key=my_app&limit=8&media_filter=tinygif,gif` ); const data = await res.json(); // Use tinygif for previews, gif for the URL sent in metadata

Setup: Create an API key in Google Cloud Console and enable the Tenor API. Use NEXT_PUBLIC_TENOR_API_KEY for client-side search, or proxy Tenor through your backend. Include client_key in all requests. Tenor requires attribution — see their attribution guide.

File storage (attachments)

The attachment-picker.tsx lets users select files. Attachments are stored in object storage — not in your database. Your frontend must upload the file first, then call onSendMessage (or your attachment endpoint) with the resulting URL.

  1. User selects file → upload to S3, GCS, R2, or your backend proxy (presigned URLs, etc.).
  2. On upload complete → call your API with url, name, size, mimeType.
  3. Backend creates Message + Attachment; UI displays via message-bubble.tsx.

Recommended: AWS S3, Google Cloud Storage, Cloudflare R2, or Azure Blob. Generate thumbnails for videos and pass thumbnailUrl for faster rendering.

Location sharing

The location-picker.tsx and location-display.tsx components handle sharing and display. Use navigator.geolocation.getCurrentPosition() to obtain coordinates, or allow manual input. Send via onSendMessage with type: "location" and metadata: { latitude, longitude, label? }.

No external API required. location-display.tsx can use OpenStreetMap for the preview and link to Google Maps for "Open in Maps".

Search docs

Search documentation by title or keywords