ComponentsCommunicationChat List

Communication

Chat List

Inbox and chat modal for 1:1 booking conversations - conversation list with search and unread badges, inline WhatsApp-style chat, optimistic local fallback from prefetched bookings, and role-aware identity so each party sees the correct counterpart.

Data sourceBackend · prefetch fallback

Fetches from the conversations API. Builder preview injects _prefetchedConversations and sets disableBackendFetch to avoid auth noise.

ChatInline modal · no navigation needed

Chat opens in a modal directly from the list row. chatScreenId is only needed for legacy external navigation.

TierFree

Available on all plans.

What it does

Four things worth knowing

Conversations are booking-keyed

Each thread is identified by a conversationId or bookingId. Keeping these keys stable is essential - navigation, unread sync, and the optimistic local fallback all depend on them. Never reassign or mutate conversation IDs between renders.

Builder preview uses prefetched data, not live auth

The builder preview path injects _prefetchedConversations (mapped from preview bookings) and sets disableBackendFetch: true to prevent end-user API calls from firing with admin/builder credentials. If the inbox is empty in preview, check the mapping in builder/[appId].tsx.

Chat is role-aware

Identity is resolved from currentUserId, JWT decode helpers, and the optional _prefetchedMembership. A provider sees the customer as the other party; a customer sees the provider. viewMode must be set correctly - do not remove the role-aware identity logic or both parties will see themselves as the sender.

Optimistic fallback keeps messages visible

When a message is sent but the backend fetch fails, the local optimistic message still appears in the thread. This prevents a blank chat after a send error. Do not remove this behaviour unless you can guarantee the backend auth path is available in every runtime context.

Builder setup

Find it, drop it, leave inline chat on

Where to find itAdd it to any messaging screen

  1. Component picker → CommunicationChat List.
  2. Drop it on the screen users navigate to for their messages.
  3. Inline chat modal works out of the box - no screen navigation to wire unless you want a full chat screen.

Before you publishThree things to check

  1. Leave chatScreenId unset unless you specifically want chat to open on a separate screen instead of the inline modal.
  2. Confirm Header Title and Search Placeholder match the language used elsewhere in the app.
  3. Verify the standalone runtime in AppPlayer.tsx injects appId, secureApiCall, currentUserEmail, and _prefetchedConversations.
Category: CommunicationComponent: BuilderChatListRegistry key: chat-listTier: Free
Props

Header, search, and navigation

PropTypeDefaultDescription
headerTitlestring’Messages’Heading displayed at the top of the inbox.
searchPlaceholderstring’Search…’Placeholder text inside the search input.
chatScreenIdstring-Navigate to a full chat screen on row tap. Leave unset to use the inline modal.
paddingnumber16Outer padding applied to the list container.
borderRadiusnumber12Corner radius applied to conversation row cards.
fontFamilystring-Font family applied to all text within the component.
Toggles & colours

Make it yours

Every colour and visibility toggle below is yours to change. The defaults are a neutral starting point - adjust them to match the brand without touching anything else.

PropDefaultWhat it controls
showSearchtrueSearch input above the conversation list.
showUnreadBadgetrueUnread count badge on each conversation row.
showTimestamptrueLast message timestamp on each conversation row.
backgroundColor#F9FAFBScreen background colour.
cardBackgroundColor#FFFFFFIndividual conversation row background.
titleColor#111827Participant name text colour.
subtitleColor#6B7280Last message preview text colour.
timestampColor#9CA3AFTimestamp text colour.
unreadBadgeColor#007AFFUnread badge background colour.
unreadBadgeTextColor#FFFFFFUnread badge count text colour.
headerTextColor#111827Header title text colour.
borderColor#E5E7EBRow card border colour.
Tips

Common mistakes to avoid

Empty inbox in builder preview

disableBackendFetch: true is set in preview to prevent auth errors. If the inbox is empty, _prefetchedConversations is not being mapped from preview bookings. Check the mapping block in builder/[appId].tsx - the bookings must be converted into the conversation shape the component expects.

401 errors in preview logs are expected

If end-user API endpoints fire with builder/admin credentials, you will see 401 Invalid token or missing tenantId. This is a signal that disableBackendFetch is not set, or the preview runtime is calling a live endpoint it should not. Fix by ensuring preview always uses prefetched data.

Do not mutate conversation IDs

Navigation, unread sync, and the optimistic fallback all key off conversationId / bookingId. If these change between renders - for example from a list re-sort that reassigns indexes - threads will duplicate or disappear. Always use stable, backend-assigned IDs.

After any code change, sync the deploy bundle

The standalone deploy bundle must stay in sync with component and runtime code. After editing BuilderChatList.tsx, the registry block, or either runtime file, run node collectSourceFiles.js from the app/ directory to update bundledSourceFiles.ts.