Skip to main content

Overview

The useChatStore hook provides access to the global chat state and actions. Built with Zustand, it includes persistence via localStorage and manages the complete chat application state.
import { useChatStore } from "@/lib/chat/state"

function Component() {
  const activeChat = useChatStore((state) => state.activeChat)
  const sendMessage = useChatStore((state) => state.sendComposerPayload)
  // ...
}

State properties

Core state

chats
Record<string, Chat>
Dictionary of all chat conversations indexed by chat ID
messages
Record<string, Message>
Dictionary of all messages indexed by message ID
contacts
Record<string, Contact>
Dictionary of all contacts indexed by contact ID
drafts
Record<string, DraftMessage>
Dictionary of draft messages indexed by chat ID
typingIndicators
TypingIndicator[]
Array of active typing indicators across all chats
searchHistory
ChatSearchHistoryItem[]
Recent search queries with timestamps
activeChatId
string | undefined
ID of the currently active/selected chat

User state

profile
Profile
Current user’s profile information
searchQuery
string
Current search input value
showArchived
boolean
Whether archived chats are visible

Methods

setActiveChat

setActiveChat: (chatId?: string) => void
Sets the active chat and marks it as read by clearing unread count.
chatId
string | undefined
Chat ID to activate, or undefined to clear active chat
Behavior:
  • Clears activeChatId if no chatId provided
  • Resets unreadCount to 0 for the selected chat
  • No-op if chatId doesn’t exist in state

setSearchQuery

setSearchQuery: (value: string) => void
Updates the search input value.
value
string
required
New search query string

pushSearchHistory

pushSearchHistory: (value: string) => void
Adds a search term to history or updates its timestamp if it already exists. Maintains up to 5 most recent searches.
value
string
required
Search term to add to history
Behavior:
  • Updates lastUsedAt timestamp if term already exists
  • Sorts history by most recently used
  • Limits history to 5 items

updateDraft

updateDraft: (chatId: string, values: Partial<DraftMessage>) => void
Updates or creates a draft message for a specific chat.
chatId
string
required
Chat ID to update draft for
values
Partial<DraftMessage>
required
Partial draft properties to update
DraftMessage shape:
{
  chatId: string
  text: string
  attachments: MediaAttachment[]
}

clearDraft

clearDraft: (chatId: string) => void
Removes the draft message for a specific chat.
chatId
string
required
Chat ID to clear draft for

sendComposerPayload

sendComposerPayload: (chatId: string, payload: ComposerPayload) => string | undefined
Creates and sends a new message, triggering automated status lifecycle progression.
chatId
string
required
Chat ID to send message to
payload
ComposerPayload
required
Message content and metadata
ComposerPayload shape:
{
  text?: string
  contentType?: MessageContentType
  media?: MediaAttachment
}
Returns: Message ID if successful, undefined if chat not found Behavior:
  • Generates unique message ID
  • Creates message with “queued” status
  • Clears draft for the chat
  • Updates chat’s lastActivityAt and lastMessagePreview
  • Schedules automatic status transitions: queued → sending → sent → delivered → read
Message status automatically progresses through lifecycle stages with delays between each transition.

receiveMessage

receiveMessage: (chatId: string, message: Message) => void
Adds an incoming message to the chat.
chatId
string
required
Chat ID to receive message in
message
Message
required
Complete message object to add
Behavior:
  • Appends message ID to chat’s messageIds array
  • Increments unreadCount (capped at 99) unless chat is active
  • Updates chat’s lastActivityAt and lastMessagePreview

markChatAsRead

markChatAsRead: (chatId: string) => void
Clears the unread count for a specific chat.
chatId
string
required
Chat ID to mark as read

setTypingIndicator

setTypingIndicator: (chatId: string, authorId: string, isTyping: boolean) => void
Manages typing indicator state for a user in a chat.
chatId
string
required
Chat ID where typing is occurring
authorId
string
required
ID of the user who is typing
isTyping
boolean
required
Whether the user is currently typing
Behavior:
  • Adds indicator with timestamp if isTyping is true and doesn’t exist
  • Removes indicator if isTyping is false and exists
  • No-op if state already matches desired state

toggleArchive

toggleArchive: (chatId: string) => void
Toggles the archived status of a chat.
chatId
string
required
Chat ID to toggle archive status

resetStore

resetStore: () => void
Resets all state to initial values and clears pending timers.
This clears all message status timers and resets to mock data state.

Persistence

The store automatically persists to localStorage with the following configuration:
  • Storage key: chat-store
  • Version: 1
  • Persisted state: chats, messages, contacts, drafts, searchHistory, profile, activeChatId, showArchived
  • Excluded from persistence: searchQuery, typingIndicators (ephemeral state)

Type definitions

interface ChatStore extends ChatStateSnapshot {
  profile: Profile
  searchQuery: string
  showArchived: boolean
  setActiveChat: (chatId?: string) => void
  setSearchQuery: (value: string) => void
  pushSearchHistory: (value: string) => void
  updateDraft: (chatId: string, values: Partial<DraftMessage>) => void
  clearDraft: (chatId: string) => void
  sendComposerPayload: (chatId: string, payload: ComposerPayload) => string | undefined
  receiveMessage: (chatId: string, message: Message) => void
  markChatAsRead: (chatId: string) => void
  setTypingIndicator: (chatId: string, authorId: string, isTyping: boolean) => void
  toggleArchive: (chatId: string) => void
  resetStore: () => void
}

Build docs developers (and LLMs) love