GuidesBackend and frontend integration

Stream responses into a frontend

Use this recipe after your backend can relay client.chat() responses. The browser consumes the native AI SDK UI message stream through your application endpoint; it never calls Blazing Agents directly.

Outcome

A React component sends only the newest message, renders text parts as they arrive, shows pending state, and reports failures without exposing backend details.

Before you begin

Complete Build a chat endpoint. Install matching AI SDK packages for the transport and React hook, and pass an application chat ID that your backend authorizes.

Connect the chat transport

Point DefaultChatTransport at the application endpoint. Its request transformer forwards the newest UIMessage plus the native submit or regeneration fields expected by the backend guide. Do not add a Blazing Agents API key to browser headers or environment variables.

Render incremental messages

Render message parts, not a custom text-delta format. useChat updates the assistant message as native chunks arrive.

Chat.tsx
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import { useState } from "react";
export function Chat({ chatId }: { chatId: string }) {
  const [input, setInput] = useState("");
  const { error, messages, sendMessage, status, stop } = useChat({
    onFinish: ({ isAbort, isError }) => { if (!isAbort && !isError) setInput(""); },
    transport: new DefaultChatTransport({
      api: `/api/chats/${chatId}`,
      prepareSendMessagesRequest: ({ messageId, messages, trigger }) =>
        ({ body: { message: messages.at(-1), messageId, trigger } }),
    }),
  });
  return <form onSubmit={e => {
    e.preventDefault(); if (input.trim()) void sendMessage({ text: input }); }}>
    {messages.map(message => <div key={message.id}>
      <strong>{message.role}:</strong>{" "}
      {message.parts.map((part, i) => part.type === "text" ? <span key={i}>{part.text}</span> : null)}
    </div>)}
    <label>Message <input value={input} onChange={e => setInput(e.target.value)} /></label>
    <button disabled={status === "submitted" || status === "streaming"}>Send</button>
    {(status === "submitted" || status === "streaming") && <button type="button" onClick={stop}>Stop</button>}
    {error && <p role="alert">The response could not be completed.</p>}
  </form>;
}

Handle completion and errors

status moves through submitted or streaming and settles at ready on completion. The input clears only after a successful finish. A native error chunk moves the hook to error while retaining the submitted text, and the Send button remains available so the End-user can retry it. Display safe application prose rather than raw backend diagnostics. stop() aborts the active request, and the backend forwards that cancellation to the Turn.

A transport reconnect is not the same as resuming a stored Session. This endpoint does not expose a reconnect route: retry a failed submission, or call regenerate() only for a successfully stored assistant message. A failed or aborted Turn does not change the Session transcript.

Verify streaming

In browser developer tools, submit a prompt that produces several words. Confirm that the response request goes only to /api/chats/..., the same assistant message renders more than once while status is streaming, and it settles once at ready. Force the backend to return an error and confirm the alert appears without a custom SSE parser or sensitive error text. The input must still contain the failed text; submit it again and confirm a new request starts, then succeeds and clears the input.

Production notes

  • Authenticate calls to the application endpoint with your normal browser session; the backend remains responsible for authorization.
  • Keep non-text parts in the message model even if this compact component does not render them. Tools and other native parts should have deliberate UI.
  • Disable duplicate submissions while a request is active and expose stop() when an End-user needs cancellation.
  • Treat regeneration as transcript mutation and authorize its application chat and target message on the backend.

SDK and REST reference

On this page