Building Real-Time Streaming AI UI and Micro-Interactions in React / Next.js with Vercel AI SDK v3

Modern users expect ChatGPT-like real-time streaming text responses and dynamic Generative UI (interactive charts, cards, and widgets) rather than static loading spinners.
In React and Next.js App Router environments, the Vercel AI SDK (v3/v4) is the industry standard toolkit for building generative AI interfaces.
This article details using useChat, streamText, Tool Calling with zod schemas, and micro-interactions to prevent layout shifts during real-time streaming.
Key Takeaways
- Streaming UI Benefit: Cuts perceived latency (TTFT) by over 80% by streaming response tokens immediately.
useChat&streamText: Synchronizes client-side React state with backend AI model streams over HTTP Data Streams without boilerplate.- Generative UI & Tool Calling: Render dynamic React components (charts, forms) directly when the LLM invokes structured tools.
1. Server Route Handler (app/api/chat/route.ts)
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';
export const runtime = 'edge';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o-mini'),
messages,
tools: {
getStockPrice: tool({
description: 'Fetch stock price and chart data',
parameters: z.object({ ticker: z.string() }),
execute: async ({ ticker }) => ({ ticker, price: 215.5, change: '+3.2%' }),
}),
},
});
return result.toDataStreamResponse();
}
2. Client Component (app/chat/page.tsx)
'use client';
import { useChat } from 'ai/react';
export default function ChatPage() {
const { messages, input, handleInputChange, handleSubmit } = useChat();
return (
<div className="p-4 max-w-xl mx-auto">
{messages.map((m) => (
<div key={m.id} className="py-2">
<strong>{m.role}: </strong>{m.content}
</div>
))}
<form onSubmit={handleSubmit} className="flex gap-2 mt-4">
<input value={input} onChange={handleInputChange} className="border p-2 flex-1" />
<button type="submit" className="bg-indigo-600 text-white px-4 py-2">Send</button>
</form>
</div>
);
}