Claude Code Plugins

Community-maintained marketplace

Feedback

openai-chatkit-frontend

@Okashanadeem/GIAIC-HACKATHON-II
0
0

>

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name openai-chatkit-frontend
description Patterns for building AI chatbot frontends using OpenAI ChatKit with Next.js App Router, including custom backend integration via ChatKit Python SDK.
version 1.0.0

OpenAI ChatKit Frontend Skill

When to use this Skill

Use this Skill whenever you are:

  • Building a chat UI for an AI-powered application.
  • Integrating OpenAI ChatKit with a Next.js App Router frontend.
  • Connecting ChatKit to a custom FastAPI backend.
  • Implementing real-time streaming chat responses.
  • Adding a conversational interface to an existing application.

This Skill works for any Next.js application that needs a production-ready chat interface with AI capabilities.

Core goals

  • Build polished, production-ready chat UIs with minimal code.
  • Integrate seamlessly with custom backends using ChatKit Python SDK.
  • Maintain responsive, accessible chat interfaces.
  • Follow consistent patterns for ChatKit configuration.
  • Enable real-time streaming for natural conversations.

Technology Stack

Component Technology Version
Frontend Framework Next.js (App Router) >=16.0.0
ChatKit React @openai/chatkit-react latest
ChatKit Core chatkit.js (CDN) latest
Backend SDK chatkit (Python) latest
Styling Tailwind CSS >=3.0

Installation

Frontend (Next.js)

npm install @openai/chatkit-react

Backend (FastAPI)

pip install chatkit openai

Required environment variable:

OPENAI_API_KEY=sk-your-api-key-here

Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                      Next.js Frontend                           │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    ChatKit Component                     │   │
│  │  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐  │   │
│  │  │  Message    │    │  Composer   │    │  Thread     │  │   │
│  │  │  List       │    │  Input      │    │  Sidebar    │  │   │
│  │  └─────────────┘    └─────────────┘    └─────────────┘  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              │                                  │
│                              ▼                                  │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  useChatKit Hook                                        │   │
│  │  - getClientSecret() → fetches token from backend       │   │
│  │  - control object → manages ChatKit state               │   │
│  └─────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                      FastAPI Backend                            │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  POST /api/chatkit/session                              │   │
│  │  - Creates ChatKit session                              │   │
│  │  - Returns client_secret for frontend                   │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              │                                  │
│                              ▼                                  │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  ChatKit Python SDK                                     │   │
│  │  - Handles AI responses                                 │   │
│  │  - Processes tool calls                                 │   │
│  │  - Manages conversation state                           │   │
│  └─────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

Two Integration Approaches

Approach 1: OpenAI-Hosted Backend (Simpler)

Use OpenAI's infrastructure to host your chat backend. Best for:

  • Quick prototypes
  • Simple chatbots without custom business logic
  • Agents built in OpenAI's Agent Builder
// Frontend only - OpenAI handles the backend
import { ChatKit, useChatKit } from '@openai/chatkit-react';

export function Chat() {
  const { control } = useChatKit({
    api: {
      async getClientSecret() {
        // Fetch from your API that calls OpenAI
        const res = await fetch('/api/chatkit/session', { method: 'POST' });
        const { client_secret } = await res.json();
        return client_secret;
      },
    },
  });

  return <ChatKit control={control} className="h-[600px]" />;
}

Approach 2: Custom Backend (Recommended for Phase III)

Use ChatKit Python SDK with your own FastAPI backend. Best for:

  • Custom business logic and tool execution
  • Integration with existing databases
  • Full control over AI behavior

This is the recommended approach for the hackathon Phase III.

Frontend Implementation

1. Add ChatKit Script

In your app/layout.tsx:

import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head>
        <Script
          src="https://cdn.platform.openai.com/deployments/chatkit/chatkit.js"
          strategy="beforeInteractive"
        />
      </head>
      <body>{children}</body>
    </html>
  );
}

2. ChatKit Component

'use client';

import { ChatKit, useChatKit } from '@openai/chatkit-react';
import { useSession } from 'next-auth/react'; // or your auth solution

interface ChatWidgetProps {
  className?: string;
}

export function ChatWidget({ className = '' }: ChatWidgetProps) {
  const { data: session } = useSession();

  const { control } = useChatKit({
    api: {
      async getClientSecret(existingSecret) {
        // Handle token refresh if existing secret is provided
        if (existingSecret) {
          // Optionally refresh the session
        }

        // Fetch new client secret from your backend
        const response = await fetch('/api/chatkit/session', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${session?.accessToken}`,
          },
          body: JSON.stringify({
            user_id: session?.user?.id,
          }),
        });

        if (!response.ok) {
          throw new Error('Failed to create chat session');
        }

        const { client_secret } = await response.json();
        return client_secret;
      },
    },
  });

  return (
    <ChatKit
      control={control}
      className={`rounded-lg shadow-lg ${className}`}
    />
  );
}

3. Chat Page

// app/chat/page.tsx
import { ChatWidget } from '@/components/ChatWidget';

export default function ChatPage() {
  return (
    <div className="container mx-auto py-8">
      <h1 className="text-2xl font-bold mb-6">Chat Assistant</h1>
      <ChatWidget className="h-[600px] w-full max-w-2xl" />
    </div>
  );
}

4. Toggle Button Integration

Add chat to existing pages with a toggle button:

'use client';

import { useState } from 'react';
import { ChatWidget } from '@/components/ChatWidget';
import { MessageCircle, X } from 'lucide-react';

export function ChatToggle() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      {/* Floating chat button */}
      <button
        onClick={() => setIsOpen(!isOpen)}
        className="fixed bottom-6 right-6 p-4 bg-blue-600 text-white rounded-full shadow-lg hover:bg-blue-700 transition-colors z-50"
        aria-label={isOpen ? 'Close chat' : 'Open chat'}
      >
        {isOpen ? <X size={24} /> : <MessageCircle size={24} />}
      </button>

      {/* Chat panel */}
      {isOpen && (
        <div className="fixed bottom-24 right-6 w-[380px] h-[500px] z-40">
          <ChatWidget className="h-full w-full" />
        </div>
      )}
    </>
  );
}

Backend Implementation (FastAPI)

Session Endpoint

# app/routers/chatkit.py
from fastapi import APIRouter, Depends, HTTPException
from openai import OpenAI
from pydantic import BaseModel
import os

router = APIRouter(prefix="/api/chatkit", tags=["chatkit"])

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])


class SessionRequest(BaseModel):
    user_id: str | None = None


class SessionResponse(BaseModel):
    client_secret: str


@router.post("/session", response_model=SessionResponse)
async def create_session(
    request: SessionRequest,
    current_user = Depends(get_current_user),
):
    """Create a ChatKit session and return client secret."""
    try:
        session = client.chatkit.sessions.create(
            # Configure your ChatKit session
            # This depends on your ChatKit setup
        )
        return SessionResponse(client_secret=session.client_secret)
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

Custom Backend with ChatKit Python SDK

For full control, implement your own chat handling:

# app/routers/chat_custom.py
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from typing import Optional

router = APIRouter(prefix="/api/{user_id}", tags=["chat"])


class ChatRequest(BaseModel):
    message: str
    conversation_id: Optional[int] = None


class ChatResponse(BaseModel):
    conversation_id: int
    response: str


@router.post("/chat", response_model=ChatResponse)
async def chat(
    user_id: str,
    request: ChatRequest,
    current_user = Depends(get_current_user),
):
    """Process chat message through AI agent."""
    # Your existing chat implementation
    # (See openai-agents-sdk-mcp-backend skill)
    pass

Styling ChatKit

Custom Theming

/* styles/chatkit.css */

/* Container styling */
openai-chatkit {
  --chatkit-primary-color: #3b82f6;
  --chatkit-background: #ffffff;
  --chatkit-text-color: #1f2937;
  --chatkit-border-radius: 0.5rem;
}

/* Dark mode support */
.dark openai-chatkit {
  --chatkit-background: #1f2937;
  --chatkit-text-color: #f3f4f6;
}

Responsive Design

export function ChatWidget() {
  return (
    <ChatKit
      control={control}
      className="
        h-[400px] w-full
        sm:h-[500px]
        md:h-[600px]
        lg:h-[700px]
      "
    />
  );
}

Error Handling

Frontend Error States

'use client';

import { ChatKit, useChatKit } from '@openai/chatkit-react';
import { useState } from 'react';

export function ChatWidget() {
  const [error, setError] = useState<string | null>(null);

  const { control } = useChatKit({
    api: {
      async getClientSecret() {
        try {
          const res = await fetch('/api/chatkit/session', { method: 'POST' });
          if (!res.ok) {
            throw new Error(`HTTP ${res.status}`);
          }
          const { client_secret } = await res.json();
          setError(null);
          return client_secret;
        } catch (e) {
          setError('Failed to connect to chat service');
          throw e;
        }
      },
    },
  });

  if (error) {
    return (
      <div className="p-4 bg-red-50 text-red-700 rounded-lg">
        <p>{error}</p>
        <button
          onClick={() => setError(null)}
          className="mt-2 text-sm underline"
        >
          Try again
        </button>
      </div>
    );
  }

  return <ChatKit control={control} className="h-[600px]" />;
}

Authentication Integration

With Better Auth

'use client';

import { ChatKit, useChatKit } from '@openai/chatkit-react';
import { useAuth } from '@/lib/auth-client';

export function AuthenticatedChat() {
  const { session, token } = useAuth();

  const { control } = useChatKit({
    api: {
      async getClientSecret() {
        if (!token) {
          throw new Error('Not authenticated');
        }

        const res = await fetch('/api/chatkit/session', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json',
          },
        });

        const { client_secret } = await res.json();
        return client_secret;
      },
    },
  });

  if (!session) {
    return <p>Please log in to use the chat assistant.</p>;
  }

  return <ChatKit control={control} className="h-[600px]" />;
}

Best Practices

DO:

  1. Load ChatKit script early - Use strategy="beforeInteractive" in Next.js.
  2. Handle authentication - Pass JWT tokens to your session endpoint.
  3. Implement error states - Show user-friendly messages on failures.
  4. Make it responsive - Adjust chat dimensions for different screens.
  5. Provide loading states - Show spinners while connecting.

DON'T:

  1. Expose API keys - Never put OPENAI_API_KEY in frontend code.
  2. Skip error handling - Always catch and display errors gracefully.
  3. Hardcode dimensions - Use responsive classes for sizing.
  4. Ignore accessibility - Ensure chat is keyboard navigable.
  5. Block the main thread - Use async/await properly.

Testing

Component Testing

import { render, screen, waitFor } from '@testing-library/react';
import { ChatWidget } from '@/components/ChatWidget';

// Mock fetch
global.fetch = jest.fn(() =>
  Promise.resolve({
    ok: true,
    json: () => Promise.resolve({ client_secret: 'test-secret' }),
  })
) as jest.Mock;

describe('ChatWidget', () => {
  it('renders without crashing', async () => {
    render(<ChatWidget />);
    await waitFor(() => {
      expect(screen.getByRole('region')).toBeInTheDocument();
    });
  });

  it('handles session creation error', async () => {
    (fetch as jest.Mock).mockRejectedValueOnce(new Error('Network error'));
    render(<ChatWidget />);
    await waitFor(() => {
      expect(screen.getByText(/failed/i)).toBeInTheDocument();
    });
  });
});

References