Skip to content
EN DE

Custom Data Parts in the Frontend

Custom Data Parts arrive in the frontend through two channels. Persistent parts appear in message.parts alongside text and tool parts. Transient parts are delivered only through the onData callback on useChat and are not stored in message history.

Persistent parts are the primary mechanism. Each assistant message can contain multiple parts of different types. You filter by part.type to render the right component — text gets a paragraph, a weather data part gets a weather card, a progress part gets a progress bar.

Transient parts are useful for ephemeral UI updates that should not persist when the user scrolls back through conversation history — things like typing indicators or background analytics events.

'use client';
import { useChat } from '@ai-sdk/react';
export function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat();
return (
<div>
{messages.map((m) => (
<div key={m.id}>
{m.parts.map((part, i) => {
if (part.type === 'text') {
return <p key={i}>{part.text}</p>;
}
if (part.type === 'data' && part.data.type === 'weather') {
return <WeatherCard key={i} data={part.data} />;
}
if (part.type === 'data' && part.data.type === 'progress') {
return (
<ProgressBar
key={i}
current={part.data.current}
total={part.data.total}
/>
);
}
return null;
})}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
</form>
</div>
);
}
const { messages } = useChat({
onData(data) {
// Fired for transient data parts only
console.log('Transient data:', data);
if (data.type === 'analytics') {
trackEvent(data.event);
}
},
});
PatternUse Case
part.type === 'text'Render text content
part.type === 'tool'Render tool invocation UI
part.type === 'data'Custom data — check part.data.type for subtypes
part.type === 'source-url'Render source citation links
part.type === 'reasoning'Render chain-of-thought
  • Always check part.type before accessing type-specific properties.
  • Use a switch or if chain in your render loop to handle each part type.
  • For type safety, define your data part shapes as TypeScript interfaces and use UIMessage<MyMetadata> generics.

Part of AI Learning — free courses from prompt to production. Jan on LinkedIn