React Chat SDK

Build your own user-facing conversation UI with headless React hooks.

Last reviewed

React Chat SDK

Use the React Chat SDK when you want to show getuserfeedback.com conversations inside your own app. You own the UI; getuserfeedback.com keeps the conversation history.

This guide builds a React conversation view that lists conversations, opens a conversation, loads older messages, and sends a message.

How authentication works

Chat requests always use a JWT bearer token created by your app.

Before you add the SDK, configure your backend or auth provider to mint a JWT for the signed-in user. That token must be intended for your getuserfeedback.com app:

aud: https://getuserfeedback.com/v1/apps/{appId}sub: your user id

If your getuserfeedback.com app uses a custom JWT claim mapping, include exactly one claim that maps to userId. The examples use sub, which is the default.

Your React app should fetch that token from your own auth flow, then pass it to the provider. The SDK sends the token on each API request:

Authorization: Bearer <token>

getuserfeedback.com verifies the token and uses the signed user id to decide which conversations to return. See User authentication for issuer, JWKS, audience, and claim setup.

1. Install

npm install @getuserfeedback/chat-react

Requires react >= 18.

The package is headless and isomorphic. It can run in React DOM, React Native, and most non-browser React environments that provide fetch.

2. Add the provider

Wrap the part of your app that renders chat:

TypeScriptchat-provider.tsx
import { GetUserFeedbackChatProvider } from "@getuserfeedback/chat-react";import type { ReactNode } from "react";export function ChatProvider({children,token,}: {children: ReactNode;token: string;}) {return (<GetUserFeedbackChatProviderclientOptions={{auth: {jwt: {token,},},}}>{children}</GetUserFeedbackChatProvider>);}

The provider creates the chat client and makes it available to all hooks below it. When your auth provider refreshes the token, render the provider with the new token.

The default API base URL is https://api.getuserfeedback.com/v1.

3. List conversations

TypeScriptconversation-list.tsx
import { useConversationList } from "@getuserfeedback/chat-react";export function ConversationList({onSelect,}: {onSelect: (conversationId: string) => void;}) {const { conversations, hasMore, loadMore, status } = useConversationList();if (status === "loading") {return <p>Loading conversations...</p>;}if (conversations.length === 0) {return <p>No conversations yet.</p>;}return (<div>{conversations.map((conversation) => (<buttonkey={conversation.id}onClick={() => onSelect(conversation.id)}type="button">{conversation.latestMessage?.content.type === "text"? conversation.latestMessage.content.text: "Conversation"}</button>))}{hasMore ? (<button onClick={() => void loadMore()} type="button">Load more</button>) : null}</div>);}

4. Open a conversation and send a message

TypeScriptconversation-thread.tsx
import type { FormEvent } from "react";import { useState } from "react";import { useConversation } from "@getuserfeedback/chat-react";export function ConversationThread({conversationId,}: {conversationId: string;}) {const [text, setText] = useState("");const conversation = useConversation({ conversationId });async function submitMessage(event: FormEvent) {event.preventDefault();const trimmed = text.trim();if (!trimmed) return;setText("");await conversation.send({text: trimmed,});}if (conversation.status === "loading") {return <p>Loading messages...</p>;}return (<section>{conversation.hasMore ? (<button onClick={() => void conversation.loadMore()} type="button">Load older messages</button>) : null}<ol>{conversation.messages.map((message) => (<li key={message.id}><strong>{message.author}</strong>{" "}{message.content.type === "text" ? message.content.text : "Message"}</li>))}</ol><form onSubmit={submitMessage}><inputonChange={(event) => setText(event.target.value)}value={text}/><button disabled={conversation.sendStatus === "sending"} type="submit">Send</button></form></section>);}

Current limits

  • Realtime updates are not supported yet. Call refresh() after an app action where you expect new messages.
  • Sending currently supports plain text only.

Advanced: proxy through your backend

By default, the SDK calls https://api.getuserfeedback.com/v1 directly from your app. You can route requests through your own backend instead.

Pass an absolute baseUrl; the SDK uses that URL exactly, including any path prefix:

<GetUserFeedbackChatProviderclientOptions={{baseUrl: "https://app.example.com/api/chat/v1",auth: {jwt: {token: appSessionToken,},},}}><AppRoutes /></GetUserFeedbackChatProvider>

Your backend should authenticate that app session token, then call getuserfeedback.com with a JWT minted for your getuserfeedback.com app and that user.

Next