
Security News
Ruby's Bundler 4.0.18 Extends Cooldown to bundle lock and bundle cache
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.
@ethora/chat-component
Advanced tools
1. npm create vite@latest 2. select name of project, select type (react/js) 3. cd project-name 4. npm i 5. npm i @ethora/chat-component 6. go to file src/App.tsx and replace it with this code
@ethora/chat-component)
React + TypeScript chat UI component powered by Ethora backend APIs and XMPP.
Use it as a standalone chat page, as an embedded widget in your existing app, or as a customizable chat foundation with your own auth and UI.
IConfig)@ethora/chat-component gives you a production-oriented chat interface with:
The package exports:
Chat (main component)XmppProvideruseUnreadlogoutServiceuseQRCodeChat, handleQRChatIduseInAppNotificationsusePushNotificationsresendMessageEthora provides hosted and customizable messaging infrastructure plus a wider product ecosystem.
| Dimension | Ethora Chat Component | Full Ethora Platform |
|---|---|---|
| Primary goal | Embed chat quickly in a React app | End-to-end product stack (chat, profiles, wallets, AI, admin) |
| Time to first chat | Minutes | Higher initial setup, broader capabilities |
| Frontend scope | Focused web chat UI package | Multi-product ecosystem and broader SDK/tooling |
| Custom UI control | High via props + custom components | High, with additional platform-specific tooling |
| Best fit | Support chat, portal messaging, embedded chat widget | Full social/messaging app platforms with extended modules |
| Tool | Command |
|---|---|
| npm | npm i @ethora/chat-component |
| yarn | yarn add @ethora/chat-component |
| pnpm | pnpm add @ethora/chat-component |
| bun | bun add @ethora/chat-component |
import { Chat, XmppProvider } from '@ethora/chat-component';
import './App.css';
export default function App() {
return (
<XmppProvider>
<Chat />
</XmppProvider>
);
}
XmppProvider)Chat relies on internals that use useXmppClient(). In real integrations, wrap Chat (or your entire app shell) with XmppProvider:
import { Chat, XmppProvider } from '@ethora/chat-component';
export default function App() {
return (
<XmppProvider>
<Chat config={{ baseUrl: 'https://api.chat.ethora.com/v1' }} />
</XmppProvider>
);
}
XmppProvider also accepts a pushNotifications prop for headless push setup (works even if Chat is not rendered yet):
import { XmppProvider } from '@ethora/chat-component';
export default function App() {
return (
<XmppProvider
config={{ baseUrl: 'https://api.chat.ethora.com/v1', initBeforeLoad: true }}
pushNotifications={{
enabled: true,
softAsk: false,
vapidPublicKey: 'PLACEHOLDER_VAPID_PUBLIC_KEY',
iconPath: '/icons/push-icon-192.png',
badgePath: '/icons/push-badge-72.png',
firebaseConfig: {
apiKey: 'PLACEHOLDER_API_KEY',
authDomain: 'PLACEHOLDER_AUTH_DOMAIN',
projectId: 'PLACEHOLDER_PROJECT_ID',
storageBucket: 'PLACEHOLDER_STORAGE_BUCKET',
messagingSenderId: 'PLACEHOLDER_MESSAGING_SENDER_ID',
appId: 'PLACEHOLDER_APP_ID',
},
}}
>
{/* Chat can be mounted later or omitted */}
<div>App shell</div>
</XmppProvider>
);
}
To avoid duplicated wss://.../ws connections, keep a single XMPP init source:
initBeforeLoad: true -> XmppProvider is the only place that initializes XMPP.initBeforeLoad: false -> Chat (useChatWrapperInit) initializes XMPP.If your app also has external client.login(...) logic, guard it:
if (chatConfig.initBeforeLoad) {
console.warn('[XMPP] initBeforeLoad=true, skip external client.login()');
return;
}
await client.login(...);
Also pass a memoized config object to both XmppProvider and Chat:
const chatConfig = useMemo(() => ({ ...baseConfig }), [baseConfig]);
<XmppProvider config={chatConfig}>
<Chat config={chatConfig} />
</XmppProvider>;
npm run dev
Open http://localhost:5173.
All modes below assume XmppProvider wraps Chat.
<XmppProvider>
<Chat />
</XmppProvider>
Useful for local proof-of-concept and quick UI validation.
If no googleLogin, no jwtLogin, no userLogin, and no defaultLogin, LoginWrapper currently triggers internal email/password fallback logic.
<XmppProvider>
<Chat config={{ colors: { primary: '#2563eb', secondary: '#dbeafe' } }} />
</XmppProvider>
user prop<XmppProvider>
<Chat
user={{
email: 'user@example.com',
password: 'PLACEHOLDER_PASSWORD',
}}
/>
</XmppProvider>
userLogin)<XmppProvider>
<Chat
config={{
userLogin: {
enabled: true,
user: {
_id: 'PLACEHOLDER_USER_ID',
appId: 'PLACEHOLDER_APP_ID',
walletAddress: 'PLACEHOLDER_WALLET_ADDRESS',
defaultWallet: { walletAddress: 'PLACEHOLDER_WALLET_ADDRESS' },
firstName: 'Jane',
lastName: 'Doe',
xmppPassword: 'PLACEHOLDER_XMPP_PASSWORD',
token: 'PLACEHOLDER_ACCESS_TOKEN',
refreshToken: 'PLACEHOLDER_REFRESH_TOKEN',
username: 'PLACEHOLDER_USERNAME',
},
},
}}
/>
</XmppProvider>
<XmppProvider>
<Chat
config={{
jwtLogin: {
enabled: true,
token: 'PLACEHOLDER_JWT_TOKEN',
},
}}
/>
</XmppProvider>
<XmppProvider>
<Chat
config={{
googleLogin: {
enabled: true,
firebaseConfig: {
apiKey: 'PLACEHOLDER_API_KEY',
authDomain: 'PLACEHOLDER_AUTH_DOMAIN',
projectId: 'PLACEHOLDER_PROJECT_ID',
storageBucket: 'PLACEHOLDER_STORAGE_BUCKET',
messagingSenderId: 'PLACEHOLDER_MESSAGING_SENDER_ID',
appId: 'PLACEHOLDER_APP_ID',
},
},
}}
/>
</XmppProvider>
<XmppProvider>
<Chat
roomJID="ROOM_JID@conference.xmpp.chat.ethora.com"
config={{
setRoomJidInPath: true,
qrUrl: 'https://your-app.example/chat/?qrChatId=',
}}
/>
</XmppProvider>
roomJID forces entry room.
setRoomJidInPath syncs room identity to URL path.
useQRCodeChat / handleQRChatId support QR/deep-link room opening.
newArch is now default-on. If omitted, runtime uses new architecture paths.config.newArch = false.defaultLogin currently has legacy inverted behavior in LoginWrapper:
defaultLogin is not set.userLogin / jwtLogin / googleLogin.These are the top-level props accepted by Chat (exported from ReduxWrapper).
| Prop | Type | Required | Notes |
|---|---|---|---|
config | IConfig | No | Main behavior/configuration object. |
roomJID | string | No | Force specific room JID on load. |
user | { email: string; password: string } | No | Credentials for email/password login helper path. |
loginData | { email: string; password: string } | No | Optional login payload. |
MainComponentStyles | React.CSSProperties | No | Outer container style override. |
token | string | No | Optional token input (legacy/integration-specific usage). |
CustomMessageComponent | React.ComponentType<MessageProps> | No | Replace message bubble rendering. |
CustomInputComponent | React.ComponentType<SendInputProps & { onSendMessage?; onSendMedia?; placeholderText?; }> | No | Replace chat input area. |
CustomScrollableArea | React.ComponentType<CustomScrollableAreaProps> | No | Replace list/scroll wrapper behavior. |
CustomDaySeparator | React.ComponentType<DaySeparatorProps> | No | Replace day separator node. |
CustomNewMessageLabel | React.ComponentType<NewMessageLabelProps> | No | Replace "new message" marker. |
IConfig)Below is a grouped reference for all config options.
| Option | Type | Description |
|---|---|---|
appId | string | App identifier for backend context. |
baseUrl | string | API base URL (defaults to https://api.chat.ethora.com/v1, the Ethora Cloud production endpoint). |
customAppToken | string | Custom app token for API initialization. |
xmppSettings | { devServer; host; conference?; xmppPingOnSendEnabled? } | XMPP connectivity settings. |
initBeforeLoad | boolean | Initialize XMPP before normal chat load flow. |
clearStoreBeforeInit | boolean | Clear local store before initialization. |
newArch | boolean | Defaults to true; set false to explicitly force legacy/old architecture paths. |
useStoreConsoleEnabled | boolean | Enable verbose internal logging in console. |
| Option | Type | Description |
|---|---|---|
disableHeader | boolean | Hide chat header. |
disableMedia | boolean | Disable media sending/processing paths. |
disableRooms | boolean | Hide/disable room list area. |
disableRoomMenu | boolean | Disable room menu controls. |
disableRoomConfig | boolean | Disable room configuration actions. |
disableNewChatButton | boolean | Hide new chat/create room action. |
disableUserCount | boolean | Hide user count in header/UI. |
disableChatInfo | { disableHeader?; disableDescription?; disableType?; disableMembers?; hideMembers?; disableChatHeaderMenu? } | Fine-grained chat info panel toggles. |
chatHeaderBurgerMenu | boolean | Toggle burger menu in chat header. |
chatHeaderSettings | { hide?; disableCreate?; disableMenu?; hideSearch? } | Additional header-level controls. |
chatHeaderAdditional | { enabled: boolean; element: any } | Inject custom element into header area. |
headerLogo | string | React.ReactElement | Custom logo in header. |
headerMenu | () => void | Custom menu handler. |
headerChatMenu | () => void | Custom room header menu handler. |
colors | { primary; secondary; icons?; ownMessageBackground?; otherMessageBackground?; inputBackground? } | Theme colours. primary drives icon colours and the sender name. icons themes every accent icon/icon-button (attach, mic, active send, new-chat, header, empty-state) — defaults to primary, so set a distinct value to decouple. ownMessageBackground/otherMessageBackground/inputBackground recolour the message bubbles and input bar. |
backgroundChat | { color?; image? } | Background colour and/or image for the messages area. (image as a string URL; pass an object URL for a File.) |
typography | TypographyConfig | Font family + size for the chat UI: fontFamily (or just googleFontsFamily) is applied across all chat text incl. messages and sender names; fontSize (px) scales message text, sender names, timestamps, inputs, room names and badges. Plus loaders (googleFontsUrl/fontFaces) and weight tokens. |
fallbackScreens | { noUser?; noConnection?; noRoom? } | Replace the built-in screens (Ethora login form, "Connecting...", empty room state) with your own text or React nodes. A plain string is rendered as centered text. |
hiddenRooms | { titles?: string[]; jids?: string[] } | Hide specific rooms from the room list and unread counters (e.g. { titles: ['Main chat'] } to suppress the auto-created default room). |
roomListStyles | React.CSSProperties | Styles for room list pane. |
chatRoomStyles | React.CSSProperties | Styles for chat pane. |
noMessagesPlaceholder | React.ComponentType | Replace empty-chat placeholder component (same render position as default placeholder). |
backgroundChat | { color?: string; image?: string | File } | Chat background customization. |
bubleMessage | MessageBubble | Bubble-level style overrides (as defined in types). |
setRoomJidInPath | boolean | Sync room JID to URL path. |
qrUrl | string | Base URL for QR deep link behavior. |
| Option | Type | Description |
|---|---|---|
defaultLogin | boolean | Legacy quirk: current runtime fallback behavior is inverted; see Behavior Notes section. |
googleLogin | { enabled: boolean; firebaseConfig: FBConfig } | Google login support via Firebase config. |
jwtLogin | { token: string; enabled: boolean; handleBadlogin?: React.ReactElement } | Log user in using JWT exchange flow. |
userLogin | { enabled: boolean; user: User | null } | Inject already-authenticated user directly. |
customLogin | { enabled: boolean; loginFunction: () => Promise<User | null> } | Provide your custom async login function. |
refreshTokens | { enabled: boolean; refreshFunction?: () => Promise<{ accessToken: string; refreshToken?: string } | null> } | Token refresh strategy. |
| Option | Type | Description |
|---|---|---|
defaultRooms | ConfigRoom[] | Seed/default rooms. |
customRooms | { rooms: PartialRoomWithMandatoryKeys[]; disableGetRooms?: boolean; singleRoom: boolean } | Fully controlled room source. |
forceSetRoom | boolean | Force room setup path in init flow. |
enableRoomsRetry | { enabled: boolean; helperText: string } | Enable retry UX when rooms fail to load. |
| Option | Type | Description |
|---|---|---|
disableInteractions | boolean | Disable message interaction menu/actions. |
disableProfilesInteractions | boolean | Disable profile interactions from chat UI. |
disableSentLogic | boolean | Disable default sent-state logic when needed. |
secondarySendButton | { enabled: boolean; messageEdit: string; label?: React.ReactNode; buttonStyles?: React.CSSProperties; hideInputSendButton?: boolean; overwriteEnterClick?: true } | Extra send action/button config. |
botMessageAutoScroll | boolean | Force auto-scroll behavior on bot messages. |
messageTextFilter | { enabled: boolean; filterFunction: (text: string) => string } | Transform/filter outgoing message text. |
eventHandlers | { onMessageSent?; onMessageFailed?; onMessageEdited? } | Lifecycle callbacks for message operations. |
translates | { enabled: boolean; translations?: Iso639_1Codes } | Message translation-related options. |
whitelistSystemMessage | string[] | Restrict/render only selected system message types. |
customSystemMessage | React.ComponentType<MessageProps> | Replace system message component renderer. |
| Option | Type | Description |
|---|---|---|
disableTypingIndicator | boolean | Disable typing indicator UI logic. |
customTypingIndicator | { enabled: boolean; text?: string | ((usersTyping: string[]) => string); position?: 'bottom' | 'top' | 'overlay' | 'floating'; styles?: React.CSSProperties; customComponent?: React.ComponentType<{ usersTyping: string[]; text: string; isVisible: boolean; }> } | Customize typing indicator content and rendering. |
blockMessageSendingWhenProcessing | boolean | { enabled: boolean; timeout?: number; onTimeout?: (roomJID: string) => void } | Gate sends while processing in-flight state. |
| Option | Type | Description |
|---|---|---|
inAppNotifications | { enabled?; showInContext?; position?; maxNotifications?; duration?; onClick?; customComponent? } | In-app toast notification behavior and custom rendering. |
| Option | Type | Description |
|---|---|---|
pushNotifications.enabled | boolean | Enable browser push subscription flow. |
pushNotifications.vapidPublicKey | string | VAPID public key for push registration. |
pushNotifications.firebaseConfig | FBConfig | Firebase app config for push messaging. |
pushNotifications.serviceWorkerPath | string | Service worker path, default /firebase-messaging-sw.js. |
pushNotifications.serviceWorkerScope | string | Service worker scope, default /. |
pushNotifications.iconPath | string | Custom icon URL/path for OS push notifications. |
pushNotifications.badgePath | string | Custom badge URL/path for OS push notifications. Falls back to iconPath. |
pushNotifications.softAsk | boolean | Do not immediately trigger browser permission prompt. |
pushNotifications.onClick | (params) => void | Promise<void> | Callback invoked when the user clicks an OS push notification (including cold start via URL marker). |
You can replace key UI parts without forking the package.
| Override prop | Purpose |
|---|---|
CustomMessageComponent | Fully custom message bubble/row rendering. |
CustomInputComponent | Custom composer and send controls. |
CustomScrollableArea | Custom scroll/list container (virtualized or custom behavior). |
CustomDaySeparator | Custom date separator component. |
CustomNewMessageLabel | Custom "new message" divider label. |
Example:
import { Chat } from '@ethora/chat-component';
import CustomMessageBubble from './CustomMessageBubble';
import CustomChatInput from './CustomChatInput';
import CustomScrollableArea from './CustomScrollableArea';
import CustomDaySeparator from './CustomDaySeparator';
import CustomNewMessageLabel from './CustomNewMessageLabel';
export default function App() {
return (
<Chat
CustomMessageComponent={CustomMessageBubble}
CustomInputComponent={CustomChatInput}
CustomScrollableArea={CustomScrollableArea}
CustomDaySeparator={CustomDaySeparator}
CustomNewMessageLabel={CustomNewMessageLabel}
config={{
colors: { primary: '#1d4ed8', secondary: '#dbeafe' },
}}
/>
);
}
Reference example components in repository:
src/examples/customComponents/CustomMessageBubble.tsxsrc/examples/customComponents/CustomChatInput.tsxsrc/examples/customComponents/CustomScrollableArea.tsxsrc/examples/customComponents/CustomDaySeparator.tsxsrc/examples/customComponents/CustomNewMessageLabel.tsx| Requirement | Why |
|---|---|
| HTTPS origin (or localhost) | Browser push APIs require secure contexts. |
| Firebase project | FCM token + push transport setup. |
| VAPID public key | Required for web push subscription. |
| Service worker file | Required for background notification handling. |
npx @ethora/chat-component ethora-chat
config:<Chat
config={{
pushNotifications: {
enabled: true,
vapidPublicKey: 'PLACEHOLDER_VAPID_PUBLIC_KEY',
firebaseConfig: {
apiKey: 'PLACEHOLDER_API_KEY',
authDomain: 'PLACEHOLDER_AUTH_DOMAIN',
projectId: 'PLACEHOLDER_PROJECT_ID',
storageBucket: 'PLACEHOLDER_STORAGE_BUCKET',
messagingSenderId: 'PLACEHOLDER_MESSAGING_SENDER_ID',
appId: 'PLACEHOLDER_APP_ID',
},
serviceWorkerPath: '/firebase-messaging-sw.js',
serviceWorkerScope: '/',
iconPath: '/icons/push-icon-192.png',
badgePath: '/icons/push-badge-72.png',
softAsk: false,
onClick: async ({ roomJID, messageId, url, data }) => {
// Your app-level routing/analytics can live here.
console.log('Push clicked:', { roomJID, messageId, url, data });
},
},
}}
/>
import { usePushNotifications } from '@ethora/chat-component';
function PushPermissionButton() {
const { requestPermission } = usePushNotifications({
enabled: true,
softAsk: true,
vapidPublicKey: 'PLACEHOLDER_VAPID_PUBLIC_KEY',
});
return <button onClick={() => requestPermission()}>Enable Push</button>;
}
iconPath and badgePath should point to public, reachable assets (for example, files from your app public/ directory).
| Strategy | Config shape | Best for |
|---|---|---|
| Default fallback (legacy quirk) | no auth block / defaultLogin | Legacy/demo flows; prefer explicit auth modes in production |
| Injected user | userLogin: { enabled: true, user } | App already has authenticated user/session |
| JWT login | jwtLogin: { enabled: true, token } | Token-based backend auth flow |
| Google login | googleLogin: { enabled: true, firebaseConfig } | Google SSO using Firebase |
| Custom login function | customLogin: { enabled: true, loginFunction } | Fully custom identity provider |
<Chat
config={{
userLogin: {
enabled: true,
user: {
_id: 'PLACEHOLDER_USER_ID',
appId: 'PLACEHOLDER_APP_ID',
walletAddress: 'PLACEHOLDER_WALLET_ADDRESS',
defaultWallet: { walletAddress: 'PLACEHOLDER_WALLET_ADDRESS' },
firstName: 'Jane',
lastName: 'Doe',
xmppPassword: 'PLACEHOLDER_XMPP_PASSWORD',
token: 'PLACEHOLDER_ACCESS_TOKEN',
refreshToken: 'PLACEHOLDER_REFRESH_TOKEN',
username: 'PLACEHOLDER_USERNAME',
},
},
}}
/>
<Chat
config={{
jwtLogin: {
enabled: true,
token: 'PLACEHOLDER_JWT_TOKEN',
},
refreshTokens: {
enabled: true,
refreshFunction: async () => {
return {
accessToken: 'PLACEHOLDER_NEW_ACCESS_TOKEN',
refreshToken: 'PLACEHOLDER_NEW_REFRESH_TOKEN',
};
},
},
}}
/>
| Export | Type | Purpose |
|---|---|---|
Chat | React component | Main chat component. |
XmppProvider | React provider | Provides XMPP client context for internal hooks/state. |
useUnread | hook | Returns unread counters (totalCount, per-room maps) plus a loading flag that stays true until the room list has loaded at least once. |
logoutService | service | Programmatic logout utility. |
useQRCodeChat | hook | Handle QR-based room links. |
handleQRChatId | function | Parse/process QR chat ID from URL. |
useInAppNotifications | hook | Enables and handles in-app notifications. |
usePushNotifications | hook | Push subscription + foreground handling workflow. |
resendMessage | function | Retry sending failed/pending messages. |
Basic hook usage:
import { useUnread, logoutService } from '@ethora/chat-component';
function HeaderActions() {
const { totalCount, loading } = useUnread();
return (
<div>
<span>{loading ? 'Loading…' : `Unread: ${totalCount}`}</span>
<button onClick={() => logoutService.performLogout()}>Logout</button>
</div>
);
}
logoutService.performLogout() behavior:
chatSettingStore/logoutrooms/setLogoutStateroomHeap/clearHeapethora-xmpp-logoutXmppProvider listens to that event and disconnects active XMPP client| Area | Status in this package |
|---|---|
| One-room embedded chat | Available |
| Multi-room chat UI | Available |
| Message interactions (reply/copy/edit/delete/report/reactions) | Available |
| Typing indicator | Available |
| Profile interactions in chat | Available (can be disabled) |
| File/media attachments | Available with ongoing enhancements |
| In-app notifications | Available |
| Web push notifications | Available |
| Wallet/assets and extended social modules | Primarily in full Ethora platform |
| Model | Best for | Pros | Tradeoffs |
|---|---|---|---|
| Hosted Ethora backend | Fast time-to-market, smaller teams, MVPs | Fast setup, managed backend operations, easier push/auth onboarding | Less infrastructure-level control |
| Self-hosted Ethora stack | Regulated environments, deep infra control | Full control over infrastructure, compliance customization, internal network deployment options | Higher DevOps/maintenance overhead |
| Hybrid | Gradual migration or split workloads | Can start fast and migrate critical paths later | More architecture complexity |
This is a practical planning snapshot for cross-platform consumers. It is not a release commitment.
| Surface | Current state | Notes |
|---|---|---|
Web React (@ethora/chat-component) | Available now | This repository. |
| React Native | Via broader Ethora stack | Track platform-specific implementation in Ethora repos/docs. |
| Swift (iOS native) | Planned / ecosystem-level | Confirm status with Ethora team for production timelines. |
| Kotlin (Android native) | Planned / ecosystem-level | Confirm status with Ethora team for production timelines. |
| Flutter | Planned / ecosystem-level | Confirm status with Ethora team for production timelines. |
| Additional roadmap items | Ongoing | Media improvements, richer profile/wallet experiences, broader integration guides. |
| Issue | Likely cause | Fix |
|---|---|---|
useXmppClient must be used within an XmppProvider | Using internal XMPP-dependent logic without provider context | Wrap the app tree with XmppProvider where needed. |
| Chat loads but no rooms appear | Auth/app context mismatch or room fetching restrictions | Verify appId, user credentials/tokens, baseUrl, and customRooms settings. |
| Push permission never appears | softAsk: true without manual trigger, insecure origin, or missing VAPID key | Trigger requestPermission(), use HTTPS/localhost, set valid VAPID key. |
| Service worker not found | firebase-messaging-sw.js missing in public dir | Run npx @ethora/chat-component ethora-chat or copy file manually. |
| Login loop / auth failure | Wrong token/user object shape | Validate jwtLogin, userLogin.user, and refresh token flow. |
Same two-layer split as the Android and iOS SDKs.
Hermetic component tests, no real server, no XMPP. Run with:
npm test # one-shot
npm run test:watch # watch mode
| Where | What | Run with |
|---|---|---|
src/**/*.test.tsx | Component tests using @testing-library/react — render the component in isolation, drive it with userEvent, assert behavior | npm test |
src/test/setup.ts | Vitest global setup (jest-dom matchers, jsdom polyfills for matchMedia / IntersectionObserver / scrollIntoView) | (loaded automatically) |
src/test/renderWithProviders.tsx | Test render helper that wraps a component with a fresh Redux store + ToastProvider — no persisted-store / saga / XMPP middleware leaking between tests | (imported by tests) |
src/test/testIds.ts | Stable data-testid constants matching Android *TestTags and iOS *AccessibilityID, so a single Maestro flow exercises the same intent on either mobile platform | (imported by tests + components) |
| Component | Test | Asserts |
|---|---|---|
<Login /> | renders email + password fields and submit button | data-testid selectors all resolve |
<Login /> | shows email validation error for an invalid email | Bad email → error message + no API call fires |
<Login /> | shows password length error for short passwords | Password < 6 chars → error + no API call |
<Login /> | calls loginEmail with valid credentials | Valid creds → mocked loginEmail called once with the right args |
Gaps to cover in follow-up PRs (file an issue + a test in the same PR when you tackle one):
<Register /> — same shape as Login + Google sign-up branch<MessageBubble /> — body / deleted / sendFailed / reaction states<RoomList /> — search filter, active-room highlight, badge counts<MessageInput /> (chat input) — send callback fires, edit mode, reply mode, disabled state<URLPreviewCard /> — link extraction + image fallbackThe host app
ethora-app-reactjs
runs a Playwright suite. Its current scope is public-page smoke
(login / register / 404). The chat-component flows themselves
are not yet covered by Playwright — when they are, those tests
should resolve nodes via the same data-testid values exported
from src/test/testIds.ts here.
| Identifier | This repo | Android | iOS |
|---|---|---|---|
chat_input | ChatInputTestIds.inputField | ChatInputTestTags.INPUT_FIELD | ChatInputAccessibilityID.inputField |
chat_send_button | ChatInputTestIds.sendButton | ChatInputTestTags.SEND_BUTTON | ChatInputAccessibilityID.sendButton |
chat_attach_button | ChatInputTestIds.attachButton | ChatInputTestTags.ATTACH_BUTTON | ChatInputAccessibilityID.attachButton |
chat_message_image | MessageBubbleTestIds.mediaContent | MessageBubbleTestTags.MEDIA_CONTENT | MessageBubbleAccessibilityID.mediaContent |
rooms_list | RoomListTestIds.roomsList | RoomListViewTestTags.ROOMS_LIST | RoomListAccessibilityID.roomsList |
room_row | RoomListTestIds.roomRow | RoomListViewTestTags.ROOM_ROW | RoomListAccessibilityID.roomRow |
auth_email_input | AuthTestIds.emailInput | (web-only) | (web-only) |
auth_submit_button | AuthTestIds.submitButton | (web-only) | (web-only) |
ethora-app-reactjs, in a paired PR.AGPL. See LICENSE.txt.
FAQs
1. npm create vite@latest 2. select name of project, select type (react/js) 3. cd project-name 4. npm i 5. npm i @ethora/chat-component 6. go to file src/App.tsx and replace it with this code
The npm package @ethora/chat-component receives a total of 121 weekly downloads. As such, @ethora/chat-component popularity was classified as not popular.
We found that @ethora/chat-component demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.