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