Skip to main content

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
This cookbook gives you a production-ready streaming chat component with a custom hook, error boundaries, and optional enhancements (markdown rendering, stop button, retry).

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 ReadableStream
Mavera API key stored server-side

Step 1: API Route (Next.js App Router)

Your API route receives messages 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:
  1. Express/Fastify proxy — Your React app calls localhost:3001/api/chat; your backend proxies to Mavera.
  2. Serverless function — Vercel, Netlify, or AWS Lambda function that receives the request and calls Mavera.
Example backend (Express):
Point your React app at http://localhost:3001/api/chat via apiUrl in the hook.

Step 5: AbortController — Stop Streaming

Allow users to cancel an in-progress request:
In the UI, show a “Stop” button when isLoading and call stop() on click.

Step 6: Markdown Rendering

For richer output, render assistant content as Markdown. Use react-markdown:

Common Pitfalls

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.
Enable CORS on your proxy (Access-Control-Allow-Origin, Access-Control-Allow-Headers). Or use same-origin (API route + React on same host).
Check isMounted or use AbortController before calling setContent in the stream loop. Or wrap in a useEffect cleanup.
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