SparrowDesk

React SDK

Use @sparrowdesk/react-chat to embed the widget in React and Next.js with a typed, SSR-safe API

@sparrowdesk/react-chat is the official React package for embedding the SparrowDesk chat widget. It handles script loading, configuration, and lifecycle — so you get a typed, SSR-safe component and hooks instead of managing the widget script yourself.

For low-level control from any page, see JavaScript API.

Install

pnpm add @sparrowdesk/react-chat
# or: npm install @sparrowdesk/react-chat
# or: yarn add @sparrowdesk/react-chat

Use your SparrowDesk widget domain and token from Settings → Chat Widget in the app (same values the HTML snippet uses).

Chat component

Minimal usage:

import * as React from "react";
import { Chat } from "@sparrowdesk/react-chat";

export function App() {
  return (
    <Chat
      domain="your-workspace.sparrowdesk.com"
      token="YOUR_WIDGET_TOKEN"
      openOnInit
    />
  );
}

Behavior highlights from the package:

  • Injects the widget script once and dedupes duplicate mounts.
  • SSR-safe: no-ops when window / document are unavailable.
  • TypeScript: typed ChatProps.
  • Optional deferral: wait until user interaction before loading the script (see below).

Common props

PropNotes
domainRequired. SparrowDesk host for the widget, e.g. your-workspace.sparrowdesk.com.
tokenRequired. Widget token from SparrowDesk settings.
tagsPassed to window.sparrowDesk.setTags when the API is ready.
contactFieldsRecord → setContactFields (internal names; invalid keys/values skipped server-side).
conversationFieldsRecord → setConversationFields.
onReadyCalled when window.sparrowDesk is available; receives the API object.
onOpen / onCloseWired via onOpen / onClose on the global API.
openOnInitIf true, calls openWidget() when ready.
hideOnInitIf true, calls hideWidget() when ready.
connectOnPageLoadDefault true. If false, can delay script injection (see performance below).
initializeOnInteractionDefault true with deferred load: init on first pointerdown / keydown.
readyTimeoutMsHow long to wait for window.sparrowDesk (default 10000).
cleanupOnUnmountIf true, removes the injected script tag on unmount.

Example with fields and events:

<Chat
  domain="your-workspace.sparrowdesk.com"
  token="YOUR_WIDGET_TOKEN"
  tags={["vip", "returning-user"]}
  contactFields={{
    full_name: "Alex",
    nick_name: "Alex",
  }}
  conversationFields={{
    priority: "med",
    status: "todo",
    request_type: "ENQUIRY",
  }}
  onOpen={() => console.log("Widget opened")}
  onClose={() => console.log("Widget closed")}
/>

Provider and useSparrowDesk()

To open, close, hide, or set tags from anywhere under a subtree, wrap the app with SparrowDeskProvider and use useSparrowDesk():

import {
  SparrowDeskProvider,
  useSparrowDesk,
} from "@sparrowdesk/react-chat";

function HomePage() {
  const { openWidget, closeWidget, hideWidget, setTags } = useSparrowDesk();

  return (
    <>
      <button type="button" onClick={() => openWidget()}>Open</button>
      <button type="button" onClick={() => closeWidget()}>Close</button>
      <button type="button" onClick={() => hideWidget()}>Hide</button>
      <button type="button" onClick={() => setTags(["vip"])}>Set tags</button>
    </>
  );
}

export function App() {
  return (
    <SparrowDeskProvider
      domain="your-workspace.sparrowdesk.com"
      token="YOUR_WIDGET_TOKEN"
      openOnInit
    >
      <HomePage />
    </SparrowDeskProvider>
  );
}

Next.js (App Router)

Route segments are Server Components by default. Because Chat (and the provider) use client-side hooks, put them in a Client Component ("use client") and render it from your root layout (typically at the end of <body>).

"use client";

import { Chat } from "@sparrowdesk/react-chat";

export function SparrowDeskChat() {
  return (
    <Chat
      domain={process.env.NEXT_PUBLIC_SD_DOMAIN!}
      token={process.env.NEXT_PUBLIC_SD_TOKEN!}
    />
  );
}

For the Pages Router, you can optionally load the widget with dynamic(..., { ssr: false }) if you want to guarantee client-only rendering.

Defer loading until interaction

To avoid loading the widget script on first paint, set connectOnPageLoad={false} and use initializeOnInteraction (default true in many setups) so the script loads after the first pointer or key event. You can also pair connectOnPageLoad={false} with initializeOnInteraction={false} and call openWidget() from a button via useSparrowDesk().

Examples

Full working examples for React and Next.js are available in the SparrowDesk/chat examples directory.