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: the stable identity for this user in your appThe default setup uses sub as the user's stable app identity. If your
getuserfeedback.com app uses a custom JWT claim mapping, configure one primary
signed claim for chat, such as your app user id, account id, or another
customer-owned identity value.
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 app identity to decide which conversations to return. See User authentication for issuer, JWKS, audience, and claim setup.
1. Install
npm install @getuserfeedback/chat-reactRequires 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:
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
import { useConversations } from "@getuserfeedback/chat-react";export function ConversationList({onSelect,}: {onSelect: (conversationId: string) => void;}) {const { conversations, hasMore, loadMore, status } = useConversations();if (status === "loading") {return <p>Loading conversations...</p>;}return (<div>{conversations.length === 0 ? <p>No conversations yet.</p> : null}{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. Start a conversation
import type { FormEvent } from "react";import { useCreateConversation } from "@getuserfeedback/chat-react";export function NewConversation({onCreated,}: {onCreated: (conversationId: string) => void;}) {const newConversation = useCreateConversation();async function submitMessage(event: FormEvent) {event.preventDefault();const trimmed = newConversation.text.trim();if (!trimmed) return;const result = await newConversation.create({ text: trimmed });onCreated(result.conversation.id);}return (<form onSubmit={submitMessage}><inputonChange={(event) => newConversation.setText(event.target.value)}value={newConversation.text}/><button disabled={newConversation.status === "pending"} type="submit">Send</button>{newConversation.error ? <p>Message not sent.</p> : null}</form>);}5. Open a conversation and send a message
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}/><buttondisabled={conversation.sendState.status === "pending"}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
- Chat SDK reference — full client and hook surface
- JavaScript Chat SDK — use the isomorphic client without React
- User authentication — JWT setup and expected token audience