Streaming Custom Data
It is often useful to send additional data alongside the model’s response. For example, you may want to send status information, the message ids after storing them, or references to content that the language model is referring to. The AI SDK provides several helpers that allows you to stream additional data to the client and attach it to theUIMessage parts array:
createUIMessageStream: creates a data streamcreateUIMessageStreamResponse: creates a response object that streams datapipeUIMessageStreamToResponse: pipes a data stream to a server response object
Setting Up Type-Safe Data Streaming
First, define your custom message type with data part schemas for type safety:Streaming Data from the Server
In your server-side route handler, you can create aUIMessageStream and then pass it to createUIMessageStreamResponse:
You can also send stream data from custom backends, e.g. Python / FastAPI,
using the UI Message Stream
Protocol.
Types of Streamable Data
Data Parts (Persistent)
Regular data parts are added to the message history and appear inmessage.parts:
Sources
Sources are useful for RAG implementations where you want to show which documents or URLs were referenced:Transient Data Parts (Ephemeral)
Transient parts are sent to the client but not added to the message history. They are only accessible via theonData useChat handler:
Data Part Reconciliation
When you write to a data part with the same ID, the client automatically reconciles and updates that part. This enables powerful dynamic experiences like:- Collaborative artifacts - Update code, documents, or designs in real-time
- Progressive data loading - Show loading states that transform into final results
- Live status updates - Update progress bars, counters, or status indicators
- Interactive components - Build UI elements that evolve based on user interaction
id when writing to the stream.
Processing Data on the Client
Using the onData Callback
TheonData callback is essential for handling streaming data, especially transient parts:
onData callback. They will not appear in the message.parts array since they’re not added to message history.
Rendering Persistent Data Parts
You can filter and render data parts from the message parts array:Complete Example
Use Cases
- RAG Applications - Stream sources and retrieved documents
- Real-time Status - Show loading states and progress updates
- Collaborative Tools - Stream live updates to shared artifacts
- Analytics - Send usage data without cluttering message history
- Notifications - Display temporary alerts and status messages
Message Metadata vs Data Parts
Both message metadata and data parts allow you to send additional information alongside messages, but they serve different purposes:Message Metadata
Message metadata is best for message-level information that describes the message as a whole:- Attached at the message level via
message.metadata - Sent using the
messageMetadatacallback intoUIMessageStreamResponse - Ideal for: timestamps, model info, token usage, user context
- Type-safe with custom metadata types
Data Parts
Data parts are best for streaming dynamic arbitrary data:- Added to the message parts array via
message.parts - Streamed using
createUIMessageStreamandwriter.write() - Can be reconciled/updated using the same ID
- Support transient parts that don’t persist
- Ideal for: dynamic content, loading states, interactive components