Chapter 17 · Integrating React and Next.js
Chapter goals: Connect a Mastra Agent to a React/Next.js application — expose a streaming API route on the backend, implement a typewriter-effect chat UI on the frontend.
17.1 Architecture Overview
text
React Component ──fetch──▶ Next.js API Route ──▶ Mastra Agent
▲ │ │
└──── SSE Streaming ◀───┴──────────────────┘Mastra does not绑定 a specific frontend library — you can use Vercel AI SDK's useChat, CopilotKit, or directly consume the Mastra Client SDK.
17.2 Next.js API Route
typescript
// app/api/chat/route.ts (App Router)
import { mastra } from '@/mastra'
import { createUIMessageStreamResponse } from 'ai'
export async function POST(req: Request) {
const { messages, threadId, resourceId } = await req.json()
const agent = mastra.getAgent('support')
// streamVNext outputs a UI-message stream compatible with the AI SDK
const stream = await agent.streamVNext(messages, {
format: 'aisdk',
memory: {
resource: resourceId, // User identifier
thread: threadId, // Session identifier
},
})
return createUIMessageStreamResponse({ stream: stream.toUIMessageStream() })
}17.3 Frontend useChat Integration
tsx
// app/page.tsx
'use client'
import { useChat } from '@ai-sdk/react'
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, status } = useChat({
api: '/api/chat',
// Carry session info so the server-side memory works
body: { resourceId: 'user-123', threadId: 'thread-a' },
})
return (
<div className="chat">
{messages.map((m) => (
<div key={m.id} className={m.role}>
{/* Streaming: content arrives incrementally — natural typewriter effect */}
{m.parts.map((p, i) =>
p.type === 'text' ? <span key={i}>{p.text}</span> : null,
)}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} disabled={status !== 'ready'} />
<button type="submit" disabled={status !== 'ready'}>Send</button>
</form>
</div>
)
}17.4 Tool Call Visualization
The AI SDK message parts contain tool events, enabling intermediate states like "querying order…" to be rendered:
tsx
{m.parts.map((p, i) => {
if (p.type === 'tool-queryOrder') {
return p.state === 'output-available'
? <Tag key={i}>✅ Order found</Tag>
: <Tag key={i}>⏳ Querying order…</Tag>
}
if (p.type === 'text') return <span key={i}>{p.text}</span>
return null
})}17.5 Other Integration Paths
- Mastra Client SDK —
@mastra/client-jscalls the deployed Mastra server directly from the browser, no自建 route needed; - CopilotKit — drop-in React components with built-in streaming and tool visualization;
- Custom SSE consumer — manually consume the
ReadableStreamand render incrementally.
17.6 Chapter Summary
- Backend: Next.js API route streams the agent via
streamVNextwith AI SDK format; - Frontend:
useChathandles streaming, memory, and UI rendering自动; - Tool calls are visible in message parts for intermediate state visualization;
- Multiple integration paths: AI SDK, Mastra Client SDK, CopilotKit, or custom SSE.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What does streamVNext with format: "aisdk" produce?
2. How does the frontend know which session/thread to continue?
3. What enables the "typewriter effect" in the chat UI?
4. When should you use the Mastra Client SDK instead of a custom API route?
🛠️ Hands-On Exercises
- Build a Next.js app with a Mastra agent; connect it via useChat and verify streaming works.
- Add tool-call visualization: show "⏳ querying…" while a tool is executing, "✅ done" when the result is available.
- Switch to the Mastra Client SDK and call the same agent from a browser component without a custom API route.