Get assistant-ui running in 5 minutes with npm and your first chat component.
Quick Start

Initialize assistant-ui
Create a new project:
npx assistant-ui@latest createOr start from a template:
npx assistant-ui@latest create -t cloudThe CLI reference lists every template and example, and what each one includes.
Add to an existing project:
npx assistant-ui@latest initAdd API key
Create a .env file with your API key:
OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Developing locally with a ChatGPT Plus or Pro plan? You can skip the API key and run on your subscription instead; see ChatGPT Subscription.
Start the app
npm run devManual Setup
If you prefer not to use the CLI, you can install components manually.
Add the assistant-ui registry to your components.json so the shadcn CLI resolves components for your style. Styles whose name starts with base- receive Base UI flavored components; all other styles receive the Radix flavored ones. npx shadcn init defaults to Base UI styles (for example base-nova), so new projects need this style-aware URL for components that compile against that stack.
{
"registries": {
"@assistant-ui": "https://r.assistant-ui.com/styles/{style}/{name}.json"
}
}Existing Radix projects can keep the plain form https://r.assistant-ui.com/{name}.json as a fallback (for example npx shadcn@latest add https://r.assistant-ui.com/thread.json).
Add assistant-ui
npx shadcn@latest add @assistant-ui/thread @assistant-ui/thread-listThe @assistant-ui namespace resolves the Radix or Base UI flavor from your project's style through the style-aware registry entry in components.json. Without that entry, add by direct URL instead:
npx shadcn@latest add https://r.assistant-ui.com/base/thread.json https://r.assistant-ui.com/base/thread-list.jsonPrerequisites
- Setup React
- Setup shadcn/ui
Follow the manual installation guide to configure:
- Tailwind CSS
- TypeScript path aliases
- tw-animate-css
- The cn helper
- components.json (for CLI usage)
Main Component
npm install @assistant-ui/react@^0.15.31assistant-ui dependencies
npm install @assistant-ui/react-markdown@^0.14.24 @assistant-ui/react@^0.15.31 @base-ui/react class-variance-authority remark-gfm tw-shimmer@^0.4.14 zustandshadcn/ui dependencies
npm install @base-ui/reactSetup Backend Endpoint
Install provider SDK:
npm install ai @assistant-ui/ai-sdk @ai-sdk/openainpm install ai @assistant-ui/ai-sdk @ai-sdk/anthropicnpm install ai @assistant-ui/ai-sdk @ai-sdk/azurenpm install ai @assistant-ui/ai-sdk @ai-sdk/amazon-bedrocknpm install ai @assistant-ui/ai-sdk @ai-sdk/googlenpm install ai @assistant-ui/ai-sdk @ai-sdk/google-vertexnpm install ai @assistant-ui/ai-sdk @ai-sdk/groqnpm install ai @assistant-ui/ai-sdk @ai-sdk/fireworksnpm install ai @assistant-ui/ai-sdk @ai-sdk/coherenpm install ai @assistant-ui/ai-sdk ollama-ai-provider-v2npm install ai @assistant-ui/ai-sdk chrome-aiAdd an API endpoint:
import { openai } from "@ai-sdk/openai";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: openai("gpt-6-luna"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { anthropic } from "@ai-sdk/anthropic";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: anthropic("claude-sonnet-5"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { azure } from "@ai-sdk/azure";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: azure("your-deployment-name"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { bedrock } from "@ai-sdk/amazon-bedrock";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: bedrock("us.anthropic.claude-sonnet-5"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { google } from "@ai-sdk/google";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: google("gemini-3.8-flash"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { vertex } from "@ai-sdk/google-vertex";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: vertex("gemini-3.8-flash"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { groq } from "@ai-sdk/groq";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: groq("openai/gpt-oss-120b"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { fireworks } from "@ai-sdk/fireworks";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: fireworks("accounts/fireworks/models/llama-v3p3-70b-instruct"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { cohere } from "@ai-sdk/cohere";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: cohere("command-r-plus"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { ollama } from "ollama-ai-provider-v2";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: ollama("gpt-oss:20b"),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}import { chromeai } from "chrome-ai";
import { frontendTools } from "@assistant-ui/ai-sdk";
import { convertToModelMessages, streamText } from "ai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, system, tools } = await req.json();
const aiSDKTools = frontendTools(tools);
const result = streamText({
abortSignal: req.signal,
model: chromeai(),
system,
messages: await convertToModelMessages(messages, { tools: aiSDKTools }),
tools: aiSDKTools,
});
return result.toUIMessageStreamResponse();
}The system prompt and the tools schemas in the request body come from the browser, so any caller can change them. Treat both as untrusted input. A system prompt only guides the model, so enforce authorization, argument validation and usage limits in server code, define tools the server executes on the server rather than from the request, and authenticate the route before deploying.
Define environment variables:
OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"ANTHROPIC_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"AZURE_RESOURCE_NAME="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
AZURE_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"AWS_ACCESS_KEY_ID="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
AWS_REGION="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"GOOGLE_GENERATIVE_AI_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"GOOGLE_VERTEX_PROJECT="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
GOOGLE_VERTEX_LOCATION="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
GOOGLE_APPLICATION_CREDENTIALS="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"GROQ_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"FIREWORKS_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"COHERE_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"<none><none>If you aren't using Next.js, you can also deploy this endpoint to Cloudflare Workers, or any other serverless platform.
Use it in your app
"use client";
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/ai-sdk";
import { ThreadList } from "@/components/assistant-ui/elements/thread-list.aui";
import { Thread } from "@/components/assistant-ui/elements/thread.aui";
export default function MyApp() {
const runtime = useChatRuntime({
transport: new AssistantChatTransport({
api: "/api/chat",
}),
});
return (
<AssistantRuntimeProvider runtime={runtime}>
<div>
<ThreadList />
<Thread />
</div>
</AssistantRuntimeProvider>
);
}"use client";
// run `npx shadcn@latest add @assistant-ui/assistant-modal`
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/ai-sdk";
import { AssistantModal } from "@/components/assistant-ui/elements/assistant-modal.aui";
export default function MyApp() {
const runtime = useChatRuntime({
transport: new AssistantChatTransport({
api: "/api/chat",
}),
});
return (
<AssistantRuntimeProvider runtime={runtime}>
<AssistantModal />
</AssistantRuntimeProvider>
);
}