ChatKit JS Frontend Skill
Integrate OpenAI ChatKit UI components into Next.js applications with custom backend support.
Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│ Next.js Frontend │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ChatKit Widget │ │
│ │ • useChatKit hook with custom API config │ │
│ │ • Theming & customization │ │
│ │ • Auth token injection │ │
│ │ • Conversation history │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
│ Custom fetch with JWT
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ FastAPI Backend │
│ /api/chat (SSE streaming) │
└─────────────────────────────────────────────────────────────────────────┘
Quick Start
Installation
npm install @openai/chatkit-react
# Or with pnpm
pnpm add @openai/chatkit-react
Environment Variables
# Domain key from OpenAI (for hosted mode)
NEXT_PUBLIC_OPENAI_DOMAIN_KEY=your-domain-key
# Custom API URL (for custom backend mode)
NEXT_PUBLIC_CHAT_API_URL=http://localhost:8000/api/chat
Reference
| Pattern | Guide | |---------|-------| | Basic Setup | reference/basic-setup.md | | Custom API | reference/custom-api.md | | Theming | reference/theming.md |
Examples
| Example | Description | |---------|-------------| | examples/todo-chatbot.md | Complete todo chatbot widget |
Templates
| Template | Purpose | |----------|---------| | templates/ChatWidget.tsx | ChatKit widget component | | templates/ChatPage.tsx | Full chat page layout |
Basic ChatKit Widget
"use client";
import { ChatKit, useChatKit } from "@openai/chatkit-react";
export function ChatWidget() {
const { control } = useChatKit({
api: {
url: process.env.NEXT_PUBLIC_CHAT_API_URL || "/api/chat",
domainKey: process.env.NEXT_PUBLIC_OPENAI_DOMAIN_KEY,
},
});
return (
<ChatKit
control={control}
className="h-[600px] w-[400px] rounded-lg shadow-lg"
/>
);
}
Custom API with Authentication
"use client";
import { ChatKit, useChatKit } from "@openai/chatkit-react";
import { useSession } from "@/lib/auth-client"; // Better Auth
export function AuthenticatedChat() {
const { data: session } = useSession();
const { control } = useChatKit({
api: {
url: process.env.NEXT_PUBLIC_CHAT_API_URL || "/api/chat",
domainKey: process.env.NEXT_PUBLIC_OPENAI_DOMAIN_KEY,
// Custom fetch to inject auth token
fetch: async (input, init) => {
const token = session?.session?.token;
return fetch(input, {
...init,
headers: {
...init?.headers,
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
credentials: "include",
});
},
},
});
if (!session) {
return <div>Please sign in to use the chatbot</div>;
}
return (
<ChatKit
control={control}
className="h-full w-full"
/>
);
}
Theming
const { control } = useChatKit({
api: { /* ... */ },
theme: {
colorScheme: "dark", // or "light"
color: {
accent: {
primary: "#3b82f6", // Blue accent
level: 2,
},
grayscale: {
hue: 220,
tint: 5,
shade: 0,
},
surface: {
background: "#1f2937",
foreground: "#f9fafb",
},
},
radius: "soft", // "none", "soft", "round"
density: "normal", // "compact", "normal", "spacious"
typography: {
fontFamily: "Inter, system-ui, sans-serif",
fontFamilyMono: "JetBrains Mono, monospace",
baseSize: 16,
},
},
});
Start Screen Customization
const { control } = useChatKit({
api: { /* ... */ },
startScreen: {
greeting: "Hi! I'm your task assistant. How can I help?",
prompts: [
{
name: "View Tasks",
prompt: "Show me my pending tasks",
icon: "list",
},
{
name: "Add Task",
prompt: "Help me add a new task",
icon: "plus",
},
{
name: "Get Help",
prompt: "What can you help me with?",
icon: "question",
},
],
},
});
Header Customization
const { control } = useChatKit({
api: { /* ... */ },
header: {
enabled: true,
title: {
enabled: true,
text: "Todo Assistant",
},
leftAction: {
icon: "sidebar-left",
onClick: () => toggleSidebar(),
},
rightAction: {
icon: "settings-cog",
onClick: () => openSettings(),
},
},
});
Composer Customization
const { control } = useChatKit({
api: { /* ... */ },
composer: {
placeholder: "Ask me to manage your tasks...",
tools: [
{
id: "tasks",
label: "Tasks",
shortLabel: "Tasks",
icon: "list",
placeholderOverride: "What task would you like to add?",
pinned: true,
},
{
id: "search",
label: "Search",
shortLabel: "Search",
icon: "search",
placeholderOverride: "Search your tasks...",
pinned: true,
},
],
attachments: {
enabled: false, // Enable if backend supports
maxSize: 10 * 1024 * 1024, // 10MB
maxCount: 3,
accept: {
"image/*": [".png", ".jpg", ".jpeg"],
"application/pdf": [".pdf"],
},
},
},
});
Conversation History
const { control } = useChatKit({
api: { /* ... */ },
history: {
enabled: true,
showDelete: true,
showRename: true,
},
});
Embedding as Widget
For embedding the chatbot as a floating widget:
"use client";
import { useState } from "react";
import { ChatKit, useChatKit } from "@openai/chatkit-react";
export function FloatingChatWidget() {
const [isOpen, setIsOpen] = useState(false);
const { control } = useChatKit({
api: {
url: "/api/chat",
domainKey: process.env.NEXT_PUBLIC_OPENAI_DOMAIN_KEY,
},
});
return (
<>
{/* Toggle Button */}
<button
onClick={() => setIsOpen(!isOpen)}
className="fixed bottom-4 right-4 z-50 rounded-full bg-blue-600 p-4 text-white shadow-lg hover:bg-blue-700"
>
{isOpen ? "✕" : "💬"}
</button>
{/* Chat Widget */}
{isOpen && (
<div className="fixed bottom-20 right-4 z-50 h-[500px] w-[380px] overflow-hidden rounded-lg shadow-xl">
<ChatKit control={control} className="h-full w-full" />
</div>
)}
</>
);
}
Full Page Chat
"use client";
import { ChatKit, useChatKit } from "@openai/chatkit-react";
export default function ChatPage() {
const { control } = useChatKit({
api: {
url: "/api/chat",
domainKey: process.env.NEXT_PUBLIC_OPENAI_DOMAIN_KEY,
},
theme: {
colorScheme: "light",
},
startScreen: {
greeting: "Welcome! How can I help you today?",
},
});
return (
<div className="flex h-screen flex-col">
<header className="border-b p-4">
<h1 className="text-xl font-bold">Todo Chatbot</h1>
</header>
<main className="flex-1">
<ChatKit control={control} className="h-full w-full" />
</main>
</div>
);
}
OpenAI Domain Allowlist Setup
For production deployment:
- Deploy frontend to get production URL
- Add domain to OpenAI allowlist:
- Go to: https://platform.openai.com/settings/organization/security/domain-allowlist
- Click "Add domain"
- Enter your frontend URL (without trailing slash)
- Get domain key and add to env variables
Note: localhost typically works without domain allowlist configuration.
Error Handling
const { control, error, isLoading } = useChatKit({
api: { /* ... */ },
});
if (error) {
return <div>Error: {error.message}</div>;
}
if (isLoading) {
return <div>Loading...</div>;
}
Best Practices
- Use "use client" - ChatKit requires client-side rendering
- Configure CORS - Ensure backend allows frontend origin
- Inject auth tokens - Use custom fetch for authenticated requests
- Handle errors - Show user-friendly error states
- Customize theming - Match your app's design system
- Use start screen prompts - Guide users on what they can do
- Enable history - Allow users to continue conversations