Configuring @ethora/chat-component-rn

September 3, 2026 · View on GitHub

This document walks through the config object that <XmppProvider> and <Chat> accept in React Native. Field semantics are intentionally aligned with the web package @ethora/chat-component; fields below are the ones that are wired up and verified on RN. Anything web-only (DOM/CSS) is called out as web-only.

The canonical TypeScript surface is src/types/types.ts → IConfig. When adding a field, edit the interface there — src/types/models/config.model.ts re-exports it so legacy import paths keep working.

Table of contents

Reference example

The canonical pattern: XmppProvider holds the connection lifecycle (one place, one socket). Chat is just the UI. Most fields on Chat config are UI/behavior toggles — but a few must match what you passed into XmppProvider config (baseUrl, xmppSettings, jwtLogin/userLogin/customAppToken, refreshTokens, initBeforeLoad). The provider initializes the network; Chat reuses the singleton via context.

import { Chat, XmppProvider } from '@ethora/chat-component-rn';

const baseConfig = {
  customAppToken: token || '',
  baseUrl: config.base_url,
  xmppSettings: {
    devServer: config.dev_server,
    host: config.host,
    conference: config.conference,
  },
  jwtLogin: {
    enabled: true,
    token: token || '',
  },
  refreshTokens: { enabled: true },
  initBeforeLoad: true,
} as const;

<XmppProvider
  data-testid="xmpp-provider"
  config={baseConfig}
>

</XmppProvider>;

<Chat
  data-testid="chat-component"
  roomJID={room_jid}
  config={{
    ...baseConfig,
    newArch: true,
    disableInteractions: true,
    disableChatInfo: {
      disableHeader: false,
      disableDescription: true,   // hide chat description
      disableType: true,          // hide chat type
      disableMembers: true,       // member list shown but not clickable
      disableChatHeaderMenu: true, // hide Report / Leave
    },
    chatHeaderSettings: {
      hide: false,
      disableCreate: true,        // hide "New chat room" button
      disableMenu: true,          // hide Profile / Settings / Logout
      hideSearch: renderOneChatRoom,
    },
    clearStoreBeforeInit: true,
    disableNewChatButton: true,
    disableRoomConfig: true,
    disableProfilesInteractions: true,

    // patient single-room view
    disableRoomMenu: renderOneChatRoom,
    disableRooms: renderOneChatRoom,
    enableRoomsRetry: {
      enabled: true,
      helperText: translate('pages.patientMessages.initializing'),
    },
  }}
/>;

data-testid is web-only — RN ignores it. Use testID for native e2e drivers.

Single-init contract

To avoid duplicate XMPP WebSocket connections — same contract as the web package:

  • initBeforeLoad: trueXmppProvider is the only place that opens the socket. Chat does not re-init; it reuses the singleton from context.
  • initBeforeLoad omitted/falseChat initializes XMPP from its own useChatWrapperInit effect (legacy path). Don't combine modes — pick one.

config.xmppSettings.devServer is treated as the WebSocket host on RN; the SDK constructs wss://<devServer>/ws internally.

Config reference

Defaults below reflect what src/types/types.ts:IConfig ships with today. Boolean toggles are all "off by default" unless stated.

Core

OptionTypeDescription
appIdstringApp identifier sent in REST requests / app-token context.
baseUrlstringAPI base URL. Default: https://api.chat.ethora.com/v1. Override it for your QA/staging environment or self-hosted deployment.
customAppTokenstringApp-level JWT used in the Authorization header for endpoints like /users/login-with-email. Required for email login.
projectNamestringFree-form project label, surfaced in dev logs.

Authentication

Pick one auth mode. Mixing is undefined behavior.

OptionTypeDescription
jwtLogin{ enabled: boolean; token: string; handleBadlogin?: React.ReactElement }Exchange a client JWT via POST /users/client. Backwards-compatible — prefer userLogin/customLogin for new integrations.
userLogin{ enabled: boolean; user: User | null }Inject a pre-resolved user (your own auth flow). The user object must include token, xmppUsername, xmppPassword.
customLogin{ enabled: boolean; loginFunction: () => Promise<User | null> }Async login function the SDK calls during bootstrap.
googleLogin{ enabled: boolean; firebaseConfig: FBConfig }Google sign-in via Firebase.
defaultLoginbooleanLegacy: enables built-in login form. Ignored when one of the above is set.
refreshTokens{ enabled: boolean; refreshFunction?: () => Promise<{ accessToken; refreshToken? } | null> }Token-refresh strategy. With enabled: true and no refreshFunction, the SDK uses the canonical /users/refresh endpoint.
logout{ enabled: boolean; label?: string; confirm?: boolean | { title?; message?; confirmText?; cancelText? }; onBeforeLogout?: () => Promise<boolean | void> | boolean | void; onAfterLogout?: () => Promise<void> | void }Built-in "Sign out" item in the room-list header menu (rendered last, tinted colors.primary). Flow: close drawer → confirm (default true, stock copy) → onBeforeLogout (false cancels) → logoutService.performLogout()onAfterLogout (put host logout / navigation here — runs after the full teardown). Callback errors are logged, never thrown. Default: item hidden.

XMPP / network

OptionTypeDescription
xmppSettings.devServerstringXMPP WebSocket host. Default xmpp.chat.ethora.com.
xmppSettings.hoststringXMPP server domain (used in JIDs and SASL).
xmppSettings.conferencestringMUC conference subdomain. Default conference.<host>.
xmppSettings.xmppPingOnSendEnabledbooleanSend a ping immediately before a message to validate the socket.
xmppSettings.historyQoSHistoryQoSConfigTuning for the MAM-history preload scheduler.
disableLastReadbooleanSkip the chatjson:store private-store read/write (unread tracking off). See docs/unread-tracking.md.
historyQoSHistoryQoSConfigTop-level mirror of xmppSettings.historyQoS; either works.

Bootstrap

OptionTypeDescription
initBeforeLoadbooleanProvider owns the XMPP init — see single-init contract.
initBeforeLoadAuth.myEndpointstringOverride the /users/client style endpoint used by the bootstrap auth.
clearStoreBeforeInitbooleanWipe persisted Redux state on init. Useful when switching tenants.
newArchbooleanUse the REST-first room-loading path (faster cold start). Recommended.
useStoreConsoleEnabledbooleanStream every dispatched action to console.log. Dev-only.

Header and navigation

OptionTypeDescription
disableHeaderbooleanHide the chat-screen header entirely.
chatHeaderBurgerMenubooleanShow a burger-menu toggle in the chat header.
chatHeaderSettings.hidebooleanHide the room-list header.
chatHeaderSettings.disableCreatebooleanHide the "New chat" button.
chatHeaderSettings.disableMenubooleanHide the Profile/Settings/Logout drawer.
chatHeaderSettings.hideSearchbooleanHide the search bar above the chat list.
chatHeaderAdditional{ enabled: boolean; element: () => React.ReactNode }Inject a custom element below the header.
headerLogostring | React.ReactElementReplace the default "Chats" label with a logo / custom element.
headerMenu(() => void) | booleanBurger button on the left of the room-list header. A function shows the button and calls it (host-driven drawer); true shows it and opens the SDK's own menu — the same sheet the header avatar opens; omitted hides it. disableRoomMenu hides it either way.
headerChatMenu() => voidTap handler for the chat-screen header menu.

Room list

OptionTypeDescription
disableRoomsboolean | valueDon't render the room list (single-room mode). Truthy hides it.
disableRoomMenubooleanHide the room context menu.
disableNewChatButtonbooleanHide the "+" button to create rooms.
disableRoomConfigbooleanDisable room-settings entry points.
forceSetRoombooleanForce the initial room set even when the URL says otherwise.
defaultRoomsstring[] | ConfigRoom[]Seed rooms to join on bootstrap.
customRooms{ rooms; disableGetRooms?; singleRoom }Fully app-controlled room source. Skips REST /chats/my when disableGetRooms: true.
enableRoomsRetry{ enabled: boolean; helperText: string }Show a retry UI if the rooms list fails to load.
setRoomJidInPathbooleanweb-only. No-op on RN.
qrUrlstringBase URL the deep-link QR scanner resolves against.

Chat info panel

OptionTypeDescription
disableChatInfo.disableHeaderbooleanHide the info-screen header.
disableChatInfo.disableDescriptionbooleanHide the description row.
disableChatInfo.disableTypebooleanHide the chat-type row (public/private).
disableChatInfo.disableMembersbooleanShow members but disable tapping into a member profile.
disableChatInfo.hideMembersbooleanHide the members section entirely.
disableChatInfo.disableChatHeaderMenubooleanHide the "Report" and "Leave" overflow options.

Messaging and interactions

OptionTypeDescription
disableInteractionsbooleanDisable the long-press message-actions menu.
disableReactionsbooleanDisable emoji reactions UI.
disableProfilesInteractionsbooleanDon't link sender avatars/names to a profile screen.
disableUserCountbooleanHide the participant count in the header.
disableSentLogicbooleanSkip the optimistic "sent/sending/failed" state machine.
disableMediabooleanHide the attach button and the media-picker flows.
botMessageAutoScrollbooleanAuto-scroll to bottom when a bot message arrives, even if the user has scrolled up.
blockMessageSendingWhenProcessingboolean | { enabled; timeout?; onTimeout? }Disable the input while the last send is in flight; optional timeout that fires onTimeout(roomJID).
messageTextFilter{ enabled: boolean; filterFunction: (text: string) => string }Mutate outgoing message text (e.g. profanity filter).
secondarySendButton{ enabled; messageEdit; buttonText?; label?; buttonStyles?; hideInputSendButton?; overwriteEnterClick? }A second send action (e.g. "Send & post") next to the input.
whitelistSystemMessagestring[]Render only the listed isSystemMessage types.
customSystemMessageReact.ComponentType<MessageProps>Replace the default system-message bubble.

Typing and sending control

OptionTypeDescription
disableTypingIndicatorbooleanDisable both incoming render and outgoing composing-stanza emission.
customTypingIndicator{ enabled; text?; position?; styles?; customComponent? }Custom typing-indicator content + placement (bottom | top | overlay | floating).

Notifications

OptionTypeDescription
inAppNotifications.enabledbooleanIn-app toast on new messages.
inAppNotifications.showInContextbooleanShow a toast even when the message arrives in the currently open room.
inAppNotifications.maxNotificationsnumberCap concurrent on-screen toasts.
inAppNotifications.durationnumberAuto-dismiss after N ms.
inAppNotifications.position{ horizontal?; vertical?; offset? }Toast placement.
inAppNotifications.onClick(params) => void | Promise<void>Tap handler. Args: { roomJID, messageId, message, roomName, senderName }.
inAppNotifications.customComponentReact.ComponentType<…>Replace the default toast renderer.

Push notifications

Native push on RN uses FCM/APNs through Firebase. The host app owns the FCM/APNs token lifecycle and registers it with the SDK; the SDK is responsible for the /users/subscribe-room calls.

OptionTypeDescription
pushNotifications.enabledbooleanMaster switch.
pushNotifications.iconPathstringOS notification icon override.
pushNotifications.badgePathstringOS badge override (falls back to iconPath).
pushNotifications.firebaseConfigFBConfigFirebase config for the messaging service.
pushNotifications.onClick(params) => void | Promise<void>Fires when the user taps a push, including cold-start. Args: { roomJID?, messageId?, data?, notification? }.
pushNotifications.onNotificationPress(data) => voidLegacy alias of onClick; prefer onClick.

Theming and styling

OptionTypeDescription
colors.primarystringBrand color for active states, send button, badges.
colors.secondarystringTint color for backgrounds and chips.
colors.iconstringTint for chrome icons: attach paperclip, mic/send button, attach-sheet options, edit-message banner, burger menus, scroll-to-bottom FAB, outlined Cancel buttons in confirm modals. Falls back to colors.primary, then #0052CD.
colors.senderNamestringColor of the sender's name above incoming message bubbles (incl. threads). Falls back to colors.primary, then #0052CD.
colors.avatarstringSingle background for initials avatars (message bubbles, chat header, profile modals). Initials auto-switch between dark/white based on contrast. Omit to keep the default per-user pastel palette (hash of the name).
colors.dateLabelstringText color of the day-separator pill ("Today", "June 8"); the pill background is a light tint of it. Falls back to colors.primary, then #0052CD.
messageColor{ backgroundMessage; backgroundMessageUser; colorUser; color }Bubble background and text colors for "them" and "me".
backgroundChat{ color?: string; image?: string | ImageSourcePropType }Chat-screen background.
bubleMessageMessageBubbleBubble shape tokens — see src/types/types.ts:MessageBubble.
roomListStylesViewStyleRN style overrides for the room-list pane.
chatRoomStylesViewStyleRN style overrides for the chat pane.
keyboardVerticalOffsetnumberPass-through to KeyboardAvoidingView. Default iOS: 130 / Android: 100 in the testbed.

Translations

OptionTypeDescription
translates.enabledbooleanEnable in-chat translation UI.
translates.translationsIso639_1CodesDefault target language.
enableTranslatesbooleanShorthand for translates.enabled.

Event hooks

OptionTypeDescription
eventHandlers.onMessageSent(event) => void | Promise<void>Fires once a message clears the optimistic pending state. Args: { message, roomJID, user, messageType, metadata? }.
eventHandlers.onMessageFailed(event) => voidFires on send failure. Args: { message, roomJID, error, messageType }.
eventHandlers.onMessageEdited(event) => voidArgs: { messageId, newMessage, roomJID, user }.

⚠️ Functions are not persisted in Redux. The chat layer merges eventHandlers from props on top of the config snapshot at runtime — see src/components/MainComponents/ChatRoom.tsx.

Field-by-field gloss of the reference example

FieldWhat it does in the reference snippet
customAppToken: tokenApp-level JWT for backend requests that need it (email login, refresh, etc.).
baseUrlREST root. Match it across provider + chat or you'll get two REST clients.
xmppSettings.{devServer, host, conference}WebSocket host, JID domain, MUC subdomain. Same trio in both places.
jwtLogin.{enabled, token}Use the JWT path; the SDK calls POST /users/client with x-custom-token: <token> to mint the chat user.
refreshTokens.enabled: trueThe SDK auto-refreshes via the canonical endpoint when the API returns 401.
initBeforeLoad: trueProvider opens the socket. Chat reuses the singleton. Must be matching in both places.
newArch: trueREST-first room loading. Faster than the legacy "wait for getRoomsStanza" path.
disableInteractions: trueNo long-press menu on messages. Use this for view-only / patient chats.
disableChatInfo.*Granularly hides parts of the chat-info screen (description, type, header overflow menu).
chatHeaderSettings.*Hides "+ New chat", the user drawer; conditional hideSearch for single-room view.
clearStoreBeforeInit: trueForces a clean Redux state on launch. Good for switching tenants/users.
disableNewChatButton, disableRoomConfig, disableProfilesInteractionsLock the surface down so end-users can't navigate out of the chat into admin UI.
disableRoomMenu: renderOneChatRoom, disableRooms: renderOneChatRoomSingle-room patient view: when the boolean renderOneChatRoom is true, the room list and its menu are both hidden, so the user lands directly in the one chat they own.
enableRoomsRetry.{enabled, helperText}Show a retry UX with localized helper text if the rooms load fails.

Per-room behavior: single-room patient view

When a tenant only ever has one room per user (e.g. a patient ↔ care-team thread), set:

disableRooms: true,
disableRoomMenu: true,
disableNewChatButton: true,
chatHeaderSettings: { hide: false, disableCreate: true, disableMenu: true, hideSearch: true },

…and pass the room JID directly:

<Chat roomJID="patient-7c2f@conference.xmpp.chat.ethora.com" config={…} />

The room list code paths short-circuit and the user lands in the chat directly. If the room hasn't synced yet, enableRoomsRetry shows a retry button with the helper text you set.

See also