When to Use This
You’re building a React (or Next.js) chat interface and want to:- Stream responses character-by-character for better perceived performance
- Keep the API key server-side (via API route or backend proxy)
- Handle loading states, errors, and cancellation cleanly
- Support multiple frameworks — works with Create React App, Vite, Next.js App Router
Architecture
Prerequisites
React 18+ (for
useId, concurrent features) or Next.js 14+Backend or API route that proxies to Mavera with streaming and returns a
ReadableStreamMavera API key stored server-side
Step 1: API Route (Next.js App Router)
Your API route receivesmessages and personaId, calls Mavera with streaming, and forwards the stream to the client.
Step 2: Custom Hook — useStreamingChat
A reusable hook that manages the streaming request, accumulates content, and exposes loading/error state.
Step 3: Chat UI Component
A complete chat interface using the hook: persona selector, message list, input, streaming display, and error handling.Step 4: Vite / Create React App (No API Route)
If you don’t have Next.js, use a backend proxy or call Mavera from a separate backend service. Never put your API key in client-side code. Options:- Express/Fastify proxy — Your React app calls
localhost:3001/api/chat; your backend proxies to Mavera. - Serverless function — Vercel, Netlify, or AWS Lambda function that receives the request and calls Mavera.
http://localhost:3001/api/chat via apiUrl in the hook.
Step 5: AbortController — Stop Streaming
Allow users to cancel an in-progress request:isLoading and call stop() on click.
Step 6: Markdown Rendering
For richer output, render assistant content as Markdown. Usereact-markdown:
Common Pitfalls
Stream stops mid-response
Stream stops mid-response
Ensure your backend doesn’t buffer the response. In Node, avoid
res.json(); use res.write() for each chunk. Set Content-Type: text/plain so the client treats it as a stream.CORS errors when calling backend
CORS errors when calling backend
Enable CORS on your proxy (
Access-Control-Allow-Origin, Access-Control-Allow-Headers). Or use same-origin (API route + React on same host).State updates after unmount
State updates after unmount
Check
isMounted or use AbortController before calling setContent in the stream loop. Or wrap in a useEffect cleanup.Duplicate or missing chunks
Duplicate or missing chunks
Use
decoder.decode(value, { stream: true }) so multi-byte UTF-8 characters aren’t split across chunks.See Also
Build Persona Chat App
Full Next.js tutorial with streaming
Responses API
Streaming, tools, analysis mode
Quickstart: Chat
First streaming request
API Reference
Responses API spec