useChat on the client
The client is where the streaming magic is visible. useChat from @ai-sdk/react handles the fetch, the stream parsing, and the message state so you can focus on the UI.
useChat lifecycle
What happens between a keystroke and a rendered token.
'use client';
import { useChat } from '@ai-sdk/react';
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: '/api/chat',
});
return (
<div className="flex flex-col gap-3">
{messages.map((m) => (
<div key={m.id} className={m.role === 'user' ? 'text-right' : 'text-left'}>
<span className="font-mono text-xs opacity-60">{m.role}</span>
<div>{m.content}</div>
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} placeholder="Ask something..." />
</form>
</div>
);
}useChat owns the message list, the input state, the submission, and the streaming updates. You render, you do not orchestrate.
You can, but you would reinvent quite a lot. useChat normalizes UI messages, handles streaming parts as they arrive, deduplicates ids, cancels in-flight requests, and exposes status and error states. Ship your own only if you have a reason, because you will.
Quiz: Quiz
Loading practice…