Native React component for the Wexio web messenger. Renders inside a Shadow DOM portal for full style isolation β same WidgetShell runtime as the script-injected iframe and the <wexio-widget> web component. Same chat, same visitor identity, same backend; the only difference is where the React tree mounts.
π Website π Developer Docs
The messenger is a full two-way support surface, not just a contact form:
- Conversations & threads β real-time chat with history, read receipts and delivery states, plus multi-topic threads so a visitor can keep several questions side by side.
- Rich messages β images and galleries (with a lightbox), GIFs, inline video, voice notes, and file / PDF attachments; AI replies cite their sources.
- Composer β rich-text editing, emoji and GIF pickers, file attachments, and an in-widget voice recorder.
- Home, Help & News β a configurable Home tab (team status, quick actions, help search, news), a searchable help center, and a news feed β all driven by the operator's dashboard.
- Identity β anonymous by default, or log a known visitor in with a Google, JWT, or HMAC proof.
- Resilient & offline-aware β an offline outbox queues sends, reactions, read-acks and profile changes and converges on reconnect; a dropped connection is shown in-thread.
- Themed & localized β follows the operator's brand theme (light / dark) and ships 33 UI locales; visitors can switch language in-widget.
- Demo mode β omit the public key to render a self-contained preview with bundled mock content (great for landing pages and Storybook).
- Features
- Installation
- Quick start
- Identifying users
- Props
- Bot protection (Cloudflare Turnstile)
- Methods
- Types
- SSR
- Browser support
- Troubleshooting
- Use with other frameworks
- Author
- License
yarn add @wexio/messenger-widget-reactor
npm install @wexio/messenger-widget-reactreact >= 18 and react-dom >= 18 are peer dependencies β the widget uses the host's React tree.
Import the package on every page that should display the messenger (or on a common component used by them) and render the component. This must be done on the client side.
import { WexioWidget } from "@wexio/messenger-widget-react";
export default function App() {
return (
<>
{/* your app */}
<WexioWidget publicKey="pk_live_..." />
</>
);
}That's it β the widget mounts a floating launcher, handles its own theme/locale/state, and the operator dashboard sees the visitor immediately. The component manages its own lifecycle internally, so re-renders due to host DOM changes won't trigger a re-boot.
Pass a verified user to log a known visitor in (a known-user login / boot). Provide ONE proof β a Google FedCM id_token, a host-signed jwt, or the legacy userId + userHash HMAC pair:
<WexioWidget
publicKey="pk_live_..."
user={{
jwt: serverSignedJwt, // host-signed identity token (recommended)
name: "Ada Lovelace",
email: "ada@example.com",
}}
/>Memoise
user. Fresh object literals every render churn the env (the handshake is internally guarded against re-firing unless the proof actually changes, but it's tidier to avoid).
| Prop | Type | Description |
|---|---|---|
publicKey |
string |
Wexio integration public key (pk_live_...). Omit to render in demo mode (bundled mock content for landing pages or Storybook). |
user |
VisitorIdentity |
Verified identity. See Identifying users. |
config |
InjectableWidgetConfig |
Pre-resolved widget config. Set this if you already have the config server-rendered or fetched app-wide β skips the bootstrap fetch. |
onResize |
(size: { width: number; height: number }) => void |
Fired whenever the widget's intended dimensions change (open β closed β expanded). Use for host-side layout sync. |
onOpen |
() => void |
Fired when the visitor opens the panel (taps the launcher, peek bubble, etc.). |
onClose |
() => void |
Fired when the visitor taps the close chip. |
className |
string |
Pass-through class on the outer host <div>. Style this with normal layout CSS. |
style |
React.CSSProperties |
Pass-through inline styles for the outer host <div>. |
UI locale is operator-controlled. The widget ships 33 UI locales (English, Ukrainian, German, Spanish β incl.
es-MX, French, Italian, Dutch, Portuguese β incl.pt-BR, Swedish, Danish, Norwegian, Finnish, Polish, Czech, Slovak, Turkish, Romanian, Hungarian, Greek, Arabic, Hebrew, Hindi, Thai, Vietnamese, Indonesian, Japanese, Korean, Chinese βzh+zh-TW, plus regional English variants). The operator'slocaleStrategy(set in the Wexio dashboard) decides whether to follow the visitor's browser (AUTO), the host page's<html lang>(WEBSITE), or pin to a chosen language (DEFAULT+defaultLocale). The visitor can also override their language from the in-widget Profile tab.
When the operator enables Cloudflare Turnstile for the integration (Wexio dashboard β Security), the widget transparently runs an interaction-only challenge before the visitor handshake β no host wiring required. The launcher stays unclickable until the challenge resolves; on failure a small "couldn't verify you're human" popup appears anchored above the launcher with a retry button. The challenge token is attached to the prechat and identified-visitor handshake server-side, so operator-side rate-limiting / abuse rules apply automatically.
The component's open / close lifecycle is driven via the onOpen / onClose callbacks plus the visitor's tap on the launcher β no host-side imperative call is needed for normal use.
An imperative window.WexioWidget surface (show, hide, showSpace, setLocale, onUnreadCountChange, β¦) is on the roadmap to mirror the script loader's API, but doesn't ship in the current React package. For deep-link / programmatic control today, mount the widget conditionally from your own host state.
interface VisitorIdentity {
googleIdToken?: string; // Google FedCM id_token (preferred)
jwt?: string; // Host-signed JWT
userId?: string; // Legacy HMAC pairβ¦
userHash?: string; // β¦(HMAC-SHA256(userId, integrationSecret))
name?: string;
email?: string;
phone?: string;
attributes?: Record<string, unknown>;
}The pre-resolved widget config shape (theme, features, blocks, prechat, messenger chrome, sounds, locale strategy, bot protection). Pull it from the package:
import type { InjectableWidgetConfig } from "@wexio/messenger-widget-react";The integration's Cloudflare Turnstile bot-protection check (when the operator enables it in the Wexio dashboard) is wired automatically β the widget loads the CF script, runs the challenge before the visitor handshake, and shows a retry popup above the launcher if the challenge fails. Hosts don't need to wire anything extra.
The component renders null on the server. The Shadow-DOM portal target mounts on the first useEffect, so the widget is invisible during SSR/hydration and appears on first paint. Wrap in <Suspense fallback={null}> if you need to defer hydration in RSC-heavy apps.
Modern evergreen browsers β anything that supports Shadow DOM and ES2020. Internet Explorer is not supported.
- Check that the correct
publicKeyis being passed. - Check the messenger is active for your integration on https://app.wexio.io.
- Confirm the component renders on the client (not during SSR) β
<WexioWidget>returnsnulluntil the firstuseEffect.
- Verify you're computing
userHashserver-side asHMAC-SHA256(userId, integrationSecret). Never expose the integration secret to the browser. - Confirm the proof you pass in
useris one of:googleIdToken,jwt, oruserId+userHash. Passing multiple proofs uses the first one detected (Google β jwt β HMAC).
The public type surface is locked to entries/public.ts in the source repo. If you previously relied on undocumented props (locale, prefill, lightboxViewport, mode, configOverride, useDummyData, previewData), they are no longer exposed β they were either dashboard-only or moved to operator-side config. Remove them and the build will pass. The widget auto-resolves to production when publicKey is set and demo (bundled mock content) otherwise; UI locale is controlled by the operator's localeStrategy config and the visitor's Profile-tab language switcher.
The underlying widget runtime is a Web Component, so it works in any modern framework β even without a typed wrapper:
<wexio-widget public-key="pk_live_..."></wexio-widget>
<script type="module" src="https://cdn.wexio.io/widget/widget.js"></script>Typed wrappers for the popular frameworks:
@wexio/messenger-widget-vueβ Vue 3@wexio/messenger-widget-angularβ Angular@wexio/messenger-widget-emberβ Ember
For plain HTML / script-injection setups, paste the loader snippet from https://learn.wexio.io/docs/web-widget.
π€ Wexio (https://wexio.io)
Give a βοΈ if this package helped you!
See the Releases page for the full changelog.
This project is MIT licensed.
Created with β€οΈ by Wexio