UI Overview and Chatbot

Purpose and Scope

AI SDK UI is the client-facing layer for building conversational interfaces on top of AI SDK Core streams. The central reader problem on this page is turning a model stream into a usable chat experience: collecting user input, sending it to an API route, rendering partial assistant output as it arrives, and keeping enough state to disable controls or display errors at the right time. The documented entry point for this workflow is the useChat hook, which is designed to stream chat messages from an AI provider, manage chat state, and update the UI automatically as new messages arrive.

Sources: content/docs/04-ai-sdk-ui/02-chatbot.mdx

The chatbot guide presents the UI layer as a pairing between a client component and a server endpoint. The client component imports useChat from @ai-sdk/react, configures a transport, renders messages, and calls sendMessage when the user submits text. The server endpoint receives UIMessage[], converts those UI-oriented messages into model messages, runs streamText, and returns a UI message stream response. That separation is important: UI code owns interaction state and rendering, while the route handler owns provider access, instructions, and model streaming.

Relevant Source Files

  • content/docs/04-ai-sdk-ui/02-chatbot.mdx — Defines the Chatbot guide, the useChat example, the client app/page.tsx flow, the app/api/chat/route.ts streaming endpoint, message parts rendering guidance, and the initial status model used for customized UI behavior.

Core Primitives

The first primitive is useChat. It is a React hook that returns chat state and actions rather than a prebuilt UI. In the guide example, the hook returns messages, sendMessage, and status. This keeps the SDK unopinionated about layout: applications can render messages in a simple list, a custom design system, or a richer tool-aware interface. The hook also coordinates request lifecycle state, so the example disables the input and submit button whenever status !== 'ready', preventing duplicate submissions while a message is submitted or streamed.

The second primitive is the transport. The example constructs new DefaultChatTransport({ api: '/api/chat' }) and passes it to useChat. A transport is the boundary between browser state and the application’s chat endpoint. By configuring the API path explicitly, the UI hook does not need to know which provider, model, or framework route implementation is behind the endpoint. This also makes the same chat state model work with different server implementations, provided the route speaks the UI message stream protocol returned by AI SDK helpers.

The third primitive is UIMessage. UI messages are shaped for rendering and interaction, not just for provider calls. The guide highlights the parts property and recommends rendering from message.parts instead of a legacy flat content field. Parts make the message model extensible: a text part can be displayed immediately, while other part types can represent tool invocation, tool results, or richer UI-specific content. Even in the minimal chatbot, the example iterates through each part and renders only part.type === 'text', which leaves room for later tool-aware rendering without changing the message container model.

Execution Flow

A basic chatbot request starts when the user submits the form in the client component. The example prevents the default form action, checks that the input is not blank, calls sendMessage({ text: input }), and clears local input state. From that point, useChat sends the message through the configured DefaultChatTransport to /api/chat. The hook keeps the UI responsive by updating its managed state while the request is submitted and while the response stream is received, so the component can render messages as they change rather than waiting for a completed assistant response.

On the server side, the route handler reads { messages }: { messages: UIMessage[] } from the request body. It then calls streamText with a model, an instructions string, and messages: await convertToModelMessages(messages). This conversion step is the bridge from UI message structure to the model-facing message format used by AI SDK Core. The result from streamText exposes a stream, which the route wraps with toUIMessageStream and returns through createUIMessageStreamResponse. The route also exports maxDuration = 30, documenting the example’s streaming response limit.

'use client';
 
import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
 
const { messages, sendMessage, status } = useChat({
  transport: new DefaultChatTransport({
    api: '/api/chat',
  }),
});
import {
  convertToModelMessages,
  createUIMessageStreamResponse,
  streamText,
  toUIMessageStream,
  UIMessage,
} from 'ai';

API Components

ComponentWhere it runsRole in the chatbot flow
useChatClient componentManages chat state, exposes messages, sendMessage, status, and drives streaming UI updates.
DefaultChatTransportClient componentSends chat requests to the configured API endpoint, such as /api/chat.
UIMessageClient and server boundaryRepresents renderable chat messages, including the parts array used for text and richer message content.
convertToModelMessagesServer routeConverts UI messages into the message format expected by model generation calls.
streamTextServer routeStarts the provider-backed text generation stream with model, instructions, and messages.
toUIMessageStreamServer routeAdapts the core model stream into the UI message stream shape.
createUIMessageStreamResponseServer routeProduces the HTTP response consumed by the UI transport and hook.

The status value is the simplest customization point shown in the source page. The guide states that useChat returns a status and begins listing lifecycle values such as submitted, where the user message has been sent and the application is waiting for the response stream to begin. In practice, treating status as a UI contract lets the page coordinate disabled controls, loading indicators, optimistic rendering, and stop or retry buttons. The minimal example only disables the input, but the same signal can drive a full chat shell.

Implementation Details and Design Constraints

The recommended rendering path is message parts, not a single content string. This is more than a display detail: it is the UI foundation for advanced chatbot features. A text-only prototype can render text parts and ignore everything else, as the example does. Later, the same component tree can add branches for tool invocation or tool result parts. That means teams can start with a minimal streaming chatbot and grow toward tool usage without replacing the message storage and rendering model.

The route handler should be understood as an application-owned adapter around AI SDK Core. The SDK provides helpers for conversion, streaming, and response creation, but the application still chooses the model, provider import, system instructions, request validation, and deployment constraints. The example includes placeholder provider imports and model selection, which indicates that the UI workflow is provider-agnostic. Provider-specific setup belongs behind the /api/chat route, while the client only depends on the UI message contract and transport endpoint.

Official AI SDK UI guidance around persistence and stream resumption extends the same architecture. Message persistence stores and reloads chat messages around useChat, while resumable streams require application storage for messages and active streams. Those features are not prerequisites for the basic chatbot, but the basic design leaves space for them: keep UI messages as the durable boundary, keep generation work on the server, and return UI message streams that the client can consume incrementally. For long-running generations, applications should distinguish a browser disconnect from intentional cancellation.

Next Steps

Start with the example shape from the Chatbot guide: a client component using useChat and a route handler returning createUIMessageStreamResponse. Once that works, refine rendering around message.parts, add status-specific UI states, and then move to specialized UI pages for tool usage, message persistence, resumable streams, custom data, metadata, and transport behavior. If you are designing a production chatbot, treat this page as the minimal loop and add persistence, authorization, error handling, and stop or resume semantics deliberately rather than hiding them inside the first prototype.