Integrate OpenAI ChatKit React components into Next.js applications. Covers custom API backend configuration, theming, widget embedding, conversation history, and authentication integration.
Integrate OpenAI ChatKit UI components into Next.js applications with custom backend support.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 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) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
npm install @openai/chatkit-react
# Or with pnpm
pnpm add @openai/chatkit-react
# 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
| Pattern | Guide |
|---|---|
| Basic Setup | reference/basic-setup.md |
| Custom API | reference/custom-api.md |
| Theming | reference/theming.md |
| Example | Description |
|---|---|
| examples/todo-chatbot.md | Complete todo chatbot widget |
| Template | Purpose |
|---|---|
| templates/ChatWidget.tsx | ChatKit widget component |
| templates/ChatPage.tsx | Full chat page layout |
"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"
/>
);
}
"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"
/>
);
}
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,
},
},
});
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",
},
],
},
});
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(),
},
},
});
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"],
},
},
},
});
const { control } = useChatKit({
api: { /* ... */ },
history: {
enabled: true,
showDelete: true,
showRename: true,
},
});
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>
)}
</>
);
}
"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>
);
}
For production deployment:
Note: localhost typically works without domain allowlist configuration.
const { control, error, isLoading } = useChatKit({
api: { /* ... */ },
});
if (error) {
return <div>Error: {error.message}</div>;
}
if (isLoading) {
return <div>Loading...</div>;
}