Installation

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

Quick Start

Running npx assistant-ui@latest create, adding an API key to .env, starting npm run dev, then sending a first message in the new chat app

Initialize assistant-ui

Create a new project:

npx assistant-ui@latest create

Or start from a template:

npx assistant-ui@latest create -t cloud

The CLI reference lists every template and example, and what each one includes.

Add to an existing project:

npx assistant-ui@latest init

Add 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 dev

Manual 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.

components.json
{
  "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-list

The @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.json

Setup Backend Endpoint

Install provider SDK:

npm install ai @assistant-ui/ai-sdk @ai-sdk/openai

Add an API endpoint:

/app/api/chat/route.ts
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();
}

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:

/.env.local
OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

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

/app/page.tsx
"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>
  );
}

What's Next?