跳到主要内容
非官方简体中文翻译,内容版权归 Mastra 所有,遵循 Apache-2.0 许可。

Next.js

Build a streaming chat interface
Build a streaming chat interface的直接链接

In this guide, you'll build a tool-calling AI agent using Mastra, then connect it to Next.js by importing and calling the agent directly from your routes.

You'll use AI SDK UI and AI Elements to create a beautiful, interactive chat experience.

Before you begin
Before you begin的直接链接

  • You'll need an API key from a supported model provider. If you don't have a preference, use OpenAI.
  • Install Node.js v22.13.0 or later

Create a new Next.js app (optional)
Create a new Next.js app (optional)的直接链接

If you already have a Next.js app, skip to the next step.

Run the following command to create a new Next.js app:

npx create-next-app@latest my-nextjs-agent --yes --ts --eslint --tailwind --src-dir --app --turbopack --no-react-compiler --no-import-alias

This creates a project called my-nextjs-agent, but you can replace it with any name you want.

Initialize Mastra
Initialize Mastra的直接链接

Navigate to your Next.js project:

cd my-nextjs-agent

Run mastra init. When prompted, choose a provider (e.g. OpenAI) and enter your key:

npx mastra@latest init

This creates a src/mastra folder with an example weather agent and the following files:

  • index.ts: Mastra config, including memory
  • tools/weather-tool.ts: A tool to fetch weather for a given location
  • agents/weather-agent.ts: A weather agent with a prompt that uses the tool

You'll call weather-agent.ts from your Next.js routes in the next steps.

Using Studio alongside Next.js

If you want to run Studio alongside your Next.js app and have both share the same database, update the storage URL in src/mastra/index.ts to use an absolute path:

url: 'file:/absolute/path/to/your/project/mastra.db'

Relative paths resolve based on each process's working directory, which differs between next dev and mastra dev.

Install AI SDK UI & AI elements
Install AI SDK UI & AI elements的直接链接

Install AI SDK UI along with the Mastra adapter:

npm install @mastra/ai-sdk@latest @ai-sdk/react ai

Next, initialize AI Elements. When prompted to select a component library, choose Radix UI, then accept the defaults for the remaining prompts:

npx ai-elements@latest
备注

The ai-elements command runs shadcn add against the AI Elements registry, which currently publishes Radix UI components only. Installing them into a Base UI project produces TypeScript errors, tracked in vercel/ai-elements#383.

The component library prompt only appears when your project has no components.json. If you already have one, check that its style is a Radix option, such as new-york or a radix-* style, and not a base-* style, before running the command.

This downloads the entire AI Elements UI component library into a @/components/ai-elements folder.

Create a chat route
Create a chat route的直接链接

Create src/app/api/chat/route.ts:

src/app/api/chat/route.ts
import { handleChatStream } from '@mastra/ai-sdk'
import { toAISdkMessages } from '@mastra/ai-sdk/ui'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/mastra'
import { NextResponse } from 'next/server'

const THREAD_ID = 'example-user-id'
const RESOURCE_ID = 'weather-chat'

export async function POST(req: Request) {
const params = await req.json()
const stream = await handleChatStream({
mastra,
agentId: 'weather-agent',
version: 'v7',
params: {
...params,
memory: {
...params.memory,
thread: THREAD_ID,
resource: RESOURCE_ID,
},
},
})
return createUIMessageStreamResponse({ stream })
}

export async function GET() {
const memory = await mastra.getAgentById('weather-agent').getMemory()
let response = null

try {
response = await memory?.recall({
threadId: THREAD_ID,
resourceId: RESOURCE_ID,
})
} catch {
console.log('No previous messages found.')
}

const uiMessages = toAISdkMessages(response?.messages || [], { version: 'v7' })

return NextResponse.json(uiMessages)
}

The POST route accepts a prompt and streams the agent's response back in AI SDK format, while the GET route fetches message history from memory so the UI can be hydrated when the client reloads.

Create a chat page
Create a chat page的直接链接

Create src/app/chat/page.tsx:

src/app/chat/page.tsx
'use client'

import '@/app/globals.css'
import { useEffect, useState } from 'react'
import { DefaultChatTransport, ToolUIPart } from 'ai'
import { useChat } from '@ai-sdk/react'

import {
PromptInput,
PromptInputBody,
PromptInputTextarea,
} from '@/components/ai-elements/prompt-input'

import {
Conversation,
ConversationContent,
ConversationScrollButton,
} from '@/components/ai-elements/conversation'

import { Message, MessageContent, MessageResponse } from '@/components/ai-elements/message'

import { Tool, ToolHeader, ToolContent, ToolInput, ToolOutput } from '@/components/ai-elements/tool'

function Chat() {
const [input, setInput] = useState<string>('')

const { messages, setMessages, sendMessage, status } = useChat({
transport: new DefaultChatTransport({
api: '/api/chat',
}),
})

useEffect(() => {
const fetchMessages = async () => {
const res = await fetch('/api/chat')
const data = await res.json()
setMessages([...data])
}
fetchMessages()
}, [setMessages])

const handleSubmit = async () => {
if (!input.trim()) return

sendMessage({ text: input })
setInput('')
}

return (
<div className="relative size-full h-screen w-full p-6">
<div className="flex h-full flex-col">
<Conversation className="h-full">
<ConversationContent>
{messages.map(message => (
<div key={message.id}>
{message.parts?.map((part, i) => {
if (part.type === 'text') {
return (
<Message key={`${message.id}-${i}`} from={message.role}>
<MessageContent>
<MessageResponse>{part.text}</MessageResponse>
</MessageContent>
</Message>
)
}

if (part.type?.startsWith('tool-')) {
return (
<Tool key={`${message.id}-${i}`}>
<ToolHeader
type={(part as ToolUIPart).type}
state={(part as ToolUIPart).state || 'output-available'}
className="cursor-pointer"
/>
<ToolContent>
<ToolInput input={(part as ToolUIPart).input || {}} />
<ToolOutput
output={(part as ToolUIPart).output}
errorText={(part as ToolUIPart).errorText}
/>
</ToolContent>
</Tool>
)
}

return null
})}
</div>
))}
<ConversationScrollButton />
</ConversationContent>
</Conversation>

<PromptInput onSubmit={handleSubmit} className="mt-20">
<PromptInputBody>
<PromptInputTextarea
onChange={e => setInput(e.target.value)}
className="md:leading-10"
value={input}
placeholder="Type your message..."
disabled={status !== 'ready'}
/>
</PromptInputBody>
</PromptInput>
</div>
</div>
)
}

export default Chat

This component connects useChat() to the api/chat endpoint, sending prompts there and streaming the response back in chunks.

It renders the response text using the <MessageResponse> component, and shows any tool invocations with the <Tool> component.

Test your agent
Test your agent的直接链接

  1. Run your Next.js app with npm run dev
  2. Open the chat at http://localhost:3000/chat
  3. Try asking about the weather. If your API key is set up correctly, you'll get a response

Expose the Mastra API
Expose the Mastra API的直接链接

The chat route above uses handleChatStream from @mastra/ai-sdk to stream AI SDK UI responses from a custom /api/chat endpoint. To expose Mastra's full HTTP API for agents, tools, workflows, memory, custom API routes, MCP, and A2A through the same Next.js deployment, mount the @mastra/next server adapter on a catch-all route.

Install the adapter and its Hono peer dependency:

npm install @mastra/next@latest hono

Create the catch-all route and export its HTTP method handlers:

src/app/api/[...mastra]/route.ts
import { mastra } from '@/mastra'
import { createNextRouteHandler } from '@mastra/next'

export const { GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD } = createNextRouteHandler({
mastra,
})

The prefix option defaults to /api and must match the catch-all route's mount path. For example, when mounting the adapter at src/app/api/mastra/[...mastra]/route.ts, use createNextRouteHandler({ mastra, prefix: '/api/mastra' }).

Start the app:

npm run dev

In a separate terminal, verify the adapter by asking the weather agent a question:

curl -X POST http://localhost:3000/api/agents/weather-agent/generate -H "Content-Type: application/json" -d "{\"messages\":[{\"role\":\"user\",\"content\":\"What is the weather like in Seoul?\"}]}"

The endpoint returns a complete JSON response from the agent. Keep the AI SDK UI route for the streaming chat interface, and use the catch-all route to expose the full Mastra API. The adapter is documented in full on the Next.js adapter reference page.

Next steps
Next steps的直接链接

Congratulations on building your Mastra agent with Next.js! 🎉

From here, you can extend the project with your own tools and logic:

  • Learn more about agents
  • Give your agent its own tools
  • Add human-like memory to your agent

When you're ready, read more about how Mastra integrates with AI SDK UI and Next.js, and how to deploy your agent anywhere, including Vercel: