Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ Before deploying your app, you need to verify the domain by adding it to the [Do
- Adjust starter prompts, greeting text, [chatkit theme](https://chatkit.studio/playground), and placeholder copy in [`lib/config.ts`](lib/config.ts).
- Update the event handlers inside [`components/.tsx`](components/ChatKitPanel.tsx) to integrate with your product analytics or storage.

## Troubleshooting

### Performance Issues

ChatKit uses an iframe-based architecture to maintain security isolation for API keys and credentials. While this ensures your sensitive data stays protected, it relies on cross-frame messaging (postMessage) which can be affected by browser extensions.

**If you experience slow performance or input lag:**

- **Browser Extensions**: Some extensions (content blockers, privacy tools, developer tools) can interfere with cross-frame communication. Try testing in an incognito window with extensions disabled to rule out interference.
- **Memory Usage**: Monitor browser memory in Task Manager (Shift+Esc in Chrome). Abnormal memory growth may indicate a configuration issue - please [report it](https://github.com/openai/openai-chatkit-starter-app/issues).

**For production applications**: Consider implementing a server-side integration with OpenAI's API instead of the embedded ChatKit widget. This avoids the iframe architecture entirely and provides better performance and control. See [Advanced Self-Hosting Examples](https://github.com/openai/openai-chatkit-advanced-samples) for guidance.

## References

- [ChatKit JavaScript Library](http://openai.github.io/chatkit-js/)
Expand Down
110 changes: 64 additions & 46 deletions components/ChatKitPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ChatKit, useChatKit } from "@openai/chatkit-react";
import {
STARTER_PROMPTS,
Expand Down Expand Up @@ -261,27 +261,8 @@ export function ChatKitPanel({
[isWorkflowConfigured, setErrorState]
);

const chatkit = useChatKit({
api: { getClientSecret },
theme: {
colorScheme: theme,
...getThemeConfig(theme),
},
startScreen: {
greeting: GREETING,
prompts: STARTER_PROMPTS,
},
composer: {
placeholder: PLACEHOLDER_INPUT,
attachments: {
// Enable attachments
enabled: true,
},
},
threadItemActions: {
feedback: false,
},
onClientTool: async (invocation: {
const handleClientTool = useCallback(
async (invocation: {
name: string;
params: Record<string, unknown>;
}) => {
Expand Down Expand Up @@ -314,35 +295,72 @@ export function ChatKitPanel({

return { success: false };
},
onResponseEnd: () => {
onResponseEnd();
},
onResponseStart: () => {
setErrorState({ integration: null, retryable: false });
},
onThreadChange: () => {
processedFacts.current.clear();
},
onError: ({ error }: { error: unknown }) => {
// Note that Chatkit UI handles errors for your users.
// Thus, your app code doesn't need to display errors on UI.
console.error("ChatKit error", error);
},
[onThemeRequest, onWidgetAction]
);

const handleResponseStart = useCallback(() => {
setErrorState({ integration: null, retryable: false });
}, [setErrorState]);

const handleThreadChange = useCallback(() => {
processedFacts.current.clear();
}, []);

const handleError = useCallback(({ error }: { error: unknown }) => {
console.error("ChatKit error", error);
}, []);

const themeConfig = useMemo(
() => ({
colorScheme: theme,
...getThemeConfig(theme),
}),
[theme]
);

const apiConfig = useMemo(() => ({ getClientSecret }), [getClientSecret]);

const startScreenConfig = useMemo(
() => ({
greeting: GREETING,
prompts: STARTER_PROMPTS,
}),
[]
);

const composerConfig = useMemo(
() => ({
placeholder: PLACEHOLDER_INPUT,
attachments: {
enabled: true,
},
}),
[]
);

const threadItemActionsConfig = useMemo(
() => ({
feedback: false,
}),
[]
);

const chatkit = useChatKit({
api: apiConfig,
theme: themeConfig,
startScreen: startScreenConfig,
composer: composerConfig,
threadItemActions: threadItemActionsConfig,
onClientTool: handleClientTool,
onResponseEnd: onResponseEnd,
onResponseStart: handleResponseStart,
onThreadChange: handleThreadChange,
onError: handleError,
});

const activeError = errors.session ?? errors.integration;
const blockingError = errors.script ?? activeError;

if (isDev) {
console.debug("[ChatKitPanel] render state", {
isInitializingSession,
hasControl: Boolean(chatkit.control),
scriptStatus,
hasError: Boolean(blockingError),
workflowId: WORKFLOW_ID,
});
}

return (
<div className="relative pb-8 flex h-[90vh] w-full rounded-2xl flex-col overflow-hidden bg-white shadow-sm transition-colors dark:bg-slate-900">
<ChatKit
Expand Down