Frontend Integration
Add an agent chat to your Next.js app with the React SDK — install, create a token route, drop in the chat component.
Install
pnpm add @21st-sdk/nextjs
# also installs @21st-sdk/react, ai, @ai-sdk/reactThis also installs @21st-sdk/react with all UI components. Peer dependencies: ai, @ai-sdk/react.
Token route
The SDK exchanges your secret API key for short-lived JWT tokens server-side, so credentials are never exposed to the browser.
// app/api/an-token/route.ts
import { createTokenHandler } from "@21st-sdk/nextjs/server"
export const POST = createTokenHandler({
apiKey: process.env.API_KEY_21ST!,
})API_KEY_21ST in your .env.local. Get your key from the dashboard. Never expose this key in client-side code.Chat component
Create a chat instance with createAgentChat, connect it to useChat from AI SDK, and render AgentChat.
Don't forget to import @21st-sdk/react/styles.css — it provides the base styles for the chat UI.
createAgentChat options
const chat = createAgentChat({
// Required
agent: "my-agent",
// Token exchange — pick one:
tokenUrl: "/api/an-token", // POST endpoint returning { token, expiresAt }
// getToken: async () => "...", // custom token fetcher
// Optional
apiUrl: "https://relay.an.dev", // default relay URL
sandboxId: "sbx_...", // persistent sandbox for file/session sharing
threadId: "thr_...", // specific thread within sandbox
onFinish: () => {}, // called when agent finishes
onError: (error) => {}, // called on error
})| Option | Description |
|---|---|
agent | Agent slug from the dashboard (required) |
tokenUrl | POST endpoint that returns { token, expiresAt }. Tokens are cached for 1 min. |
getToken | Custom async function returning a token string. Alternative to tokenUrl. |
sandboxId | Persistent sandbox ID — lets the agent access the same filesystem across sessions. |
threadId | Thread within the sandbox — separate conversation with shared files. |
AgentChat props
The AgentChat component renders the full chat UI — messages, input bar, tool visualizations, and streaming.
| Prop | Description |
|---|---|
messages | Chat messages from useChat() (required) |
onSend | Called when user sends a message (required) |
status | Chat status from useChat() (required) |
onStop | Stop generation callback (required) |
theme | CSS variable overrides for light/dark themes |
colorMode | "light" | "dark" | "auto" |
classNames | Per-element CSS class overrides (root, messageList, inputBar, etc.) |
slots | Replace sub-components: InputBar, UserMessage, AssistantMessage, ToolRenderer |
toolRenderers | Custom renderers for specific tool names |
modelSelector | Show a model picker if the agent allows model switching |
attachments | File upload configuration (allowed types, max size) |
Custom tool renderers
When your agent calls a custom tool, render the result with your own React component instead of the default JSON view.
| Prop | Type | Description |
|---|---|---|
name | string | Tool name |
input | Record<string, unknown> | Tool input arguments |
output | unknown | undefined | Tool output (undefined while pending) |
status | "pending" | "streaming" | "success" | "error" | Execution status |
Per-message options
Override agent runtime settings for individual messages — switch models, limit cost, or restrict tools for specific tasks.
Persistent sandboxes and threads
For apps where the agent needs to remember files or hold multiple conversations, create a sandbox on the server and pass its ID to the chat. Use threads for separate conversations within the same sandbox.
The sandbox and thread API routes use the Server SDK. See the backend integration page for the full API.