React/Next.js Integration
Learn how to integrate Chatoshi SDK with React and Next.js applications for seamless crypto assistant functionality.
React/Next.js Integration
This guide shows you how to integrate the Chatoshi SDK with React and Next.js applications. Since the SDK uses script tags, we'll use the useEffect hook to properly manage the SDK lifecycle.
Basic React Integration
Method 1: Using useEffect with Script Loading
import React, { useEffect, useRef } from 'react';
const CryptoAssistantWidget = ({
partnerKey,
mode = 'default'
}) => {
const chatRef = useRef(null);
const containerRef = useRef(null);
const sdkLoadedRef = useRef(false);
useEffect(() => {
const loadChatoshiSDK = async () => {
// Check if SDK is already loaded
if (window.Chatoshi) {
initializeChat();
return;
}
// Load SDK script
const script = document.createElement('script');
script.src = '/chatoshi-sdk.js'; // Make sure this file is in your public folder
script.async = true;
script.onload = () => {
sdkLoadedRef.current = true;
initializeChat();
};
script.onerror = () => {
console.error('Failed to load Chatoshi SDK');
};
document.head.appendChild(script);
// Cleanup function
return () => {
if (script.parentNode) {
script.parentNode.removeChild(script);
}
};
};
const initializeChat = () => {
if (!window.Chatoshi) return;
// Destroy existing chat instance
if (chatRef.current) {
chatRef.current.destroy();
}
const options = {
partnerKey,
mode,
};
// Add container for default mode
if (mode === 'default' && containerRef.current) {
options.container = containerRef.current;
}
// Initialize Chatoshi
chatRef.current = new window.Chatoshi(options);
// Event listeners
chatRef.current.on('app:ready', () => {
console.log('Chatoshi crypto assistant is ready in React!');
});
chatRef.current.on('message:sent', (data) => {
console.log('Crypto query sent:', data);
});
chatRef.current.on('message:received', (data) => {
console.log('AI response received:', data);
});
};
loadChatoshiSDK();
// Cleanup on unmount
return () => {
if (chatRef.current) {
chatRef.current.destroy();
chatRef.current = null;
}
};
}, [partnerKey, mode]);
return (
<div>
{mode === 'default' && (
<div
ref={containerRef}
style={{
width: '100%',
height: '500px',
border: '1px solid #ddd',
borderRadius: '8px'
}}
/>
)}
</div>
);
};
export default CryptoAssistantWidget;
Usage in React Component
import React from 'react';
import CryptoAssistantWidget from './components/CryptoAssistantWidget';
function App() {
return (
<div className="App">
<h1>My Crypto Trading App</h1>
{/* Inline Crypto Assistant */}
<div style={{ margin: '20px 0' }}>
<h2>Crypto Analysis Assistant</h2>
<CryptoAssistantWidget
partnerKey="your-partner-key"
mode="default"
/>
</div>
{/* Popup Crypto Assistant */}
<div style={{ margin: '20px 0' }}>
<h2>Quick Crypto Help</h2>
<CryptoAssistantWidget
partnerKey="your-partner-key"
mode="popup"
/>
</div>
</div>
);
}
export default App;
Next.js Integration
Method 1: Using Next.js Script Component
// pages/index.js or app/page.js
import { useEffect, useRef, useState } from 'react';
import Script from 'next/script';
export default function HomePage() {
const chatRef = useRef(null);
const [sdkLoaded, setSdkLoaded] = useState(false);
const initializeChat = () => {
if (!window.Chatoshi || chatRef.current) return;
chatRef.current = new window.Chatoshi({
partnerKey: 'your-partner-key',
mode: 'popup',
popupOptions: {
width: '400px',
height: '600px',
position: 'bottom-right'
}
});
chatRef.current.on('app:ready', () => {
console.log('Chatoshi crypto assistant ready in Next.js!');
});
};
useEffect(() => {
if (sdkLoaded) {
initializeChat();
}
return () => {
if (chatRef.current) {
chatRef.current.destroy();
chatRef.current = null;
}
};
}, [sdkLoaded]);
return (
<>
<Script
src="/chatoshi-sdk.js"
strategy="afterInteractive"
onLoad={() => setSdkLoaded(true)}
onError={() => console.error('Failed to load Chatoshi SDK')}
/>
<div>
<h1>My Crypto Trading App</h1>
<p>Crypto assistant will appear as a popup!</p>
</div>
</>
);
}
Method 2: Custom Hook for Chatoshi
Create a reusable hook:
// hooks/useChatoshi.js
import { useEffect, useRef, useState } from 'react';
export const useChatoshi = (options) => {
const chatRef = useRef(null);
const [isReady, setIsReady] = useState(false);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const loadAndInitialize = async () => {
setIsLoading(true);
// Load SDK if not already loaded
if (!window.Chatoshi) {
await loadSDK();
}
// Initialize chat
if (chatRef.current) {
chatRef.current.destroy();
}
chatRef.current = new window.Chatoshi(options);
chatRef.current.on('app:ready', () => {
setIsReady(true);
setIsLoading(false);
});
chatRef.current.on('app:error', (error) => {
console.error('Chatoshi error:', error);
setIsLoading(false);
});
};
const loadSDK = () => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = '/chatoshi-sdk.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
};
loadAndInitialize();
// Cleanup
return () => {
if (chatRef.current) {
chatRef.current.destroy();
chatRef.current = null;
}
setIsReady(false);
};
}, [options.partnerKey, options.mode]);
const openChat = () => {
if (chatRef.current?.open) {
chatRef.current.open();
}
};
const closeChat = () => {
if (chatRef.current?.close) {
chatRef.current.close();
}
};
const toggleChat = () => {
if (chatRef.current?.toggle) {
chatRef.current.toggle();
}
};
return {
isReady,
isLoading,
openChat,
closeChat,
toggleChat,
chatInstance: chatRef.current
};
};
Using the Custom Hook
// components/ChatComponent.js
import { useChatoshi } from '../hooks/useChatoshi';
const ChatComponent = () => {
const { isReady, isLoading, openChat, closeChat } = useChatoshi({
partnerKey: 'your-partner-key',
mode: 'drawer',
drawerOptions: {
width: '400px',
position: 'right'
}
});
return (
<div>
<h2>Chat Controls</h2>
{isLoading && <p>Loading chat...</p>}
{isReady && (
<div>
<button onClick={openChat}>Open Chat</button>
<button onClick={closeChat}>Close Chat</button>
<p>✅ Chat is ready!</p>
</div>
)}
</div>
);
};
export default ChatComponent;
Advanced Next.js Integration
App Router (Next.js 13+)
// app/components/ChatProvider.js
'use client';
import { createContext, useContext, useEffect, useRef, useState } from 'react';
const ChatContext = createContext();
export const useChatContext = () => {
const context = useContext(ChatContext);
if (!context) {
throw new Error('useChatContext must be used within ChatProvider');
}
return context;
};
export const ChatProvider = ({ children, config }) => {
const chatRef = useRef(null);
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const initChat = async () => {
// Load SDK
if (!window.Chatoshi) {
const script = document.createElement('script');
script.src = '/chatoshi-sdk.js';
await new Promise((resolve) => {
script.onload = resolve;
document.head.appendChild(script);
});
}
// Initialize
chatRef.current = new window.Chatoshi(config);
chatRef.current.on('app:ready', () => {
setIsReady(true);
});
};
initChat();
return () => {
if (chatRef.current) {
chatRef.current.destroy();
}
};
}, [config]);
return (
<ChatContext.Provider value={{
chat: chatRef.current,
isReady
}}>
{children}
</ChatContext.Provider>
);
};
Layout Integration
// app/layout.js
import { ChatProvider } from './components/ChatProvider';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<ChatProvider
config={{
partnerKey: process.env.NEXT_PUBLIC_CHATOSHI_PARTNER_KEY,
mode: 'popup'
}}
>
{children}
</ChatProvider>
</body>
</html>
);
}
Environment Variables
Create a .env.local file in your Next.js project:
# .env.local
NEXT_PUBLIC_CHATOSHI_PARTNER_ID=your-partner-id
NEXT_PUBLIC_CHATOSHI_PARTNER_SECRET=your-partner-secret
NEXT_PUBLIC_CHATOSHI_APP_ID=your-app-id
TypeScript Support
// types/chatoshi.d.ts
declare global {
interface Window {
Chatoshi: any;
}
}
// Hook with TypeScript
interface ChatoshiOptions {
partnerKey: string;
mode?: 'default' | 'popup' | 'drawer' | 'full';
container?: string | HTMLElement;
theme?: 'light' | 'dark' | 'system';
popupOptions?: {
width?: string;
height?: string;
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
};
drawerOptions?: {
width?: string;
position?: 'left' | 'right';
};
}
export const useChatoshi = (options: ChatoshiOptions) => {
// Hook implementation...
};
Best Practices
- Always cleanup: Use cleanup functions to destroy chat instances
- Check for SDK: Always verify
window.Chatoshiexists before initializing - Environment variables: Use environment variables for credentials
- Error handling: Implement proper error handling for SDK loading failures
- Loading states: Show loading indicators while SDK initializes
- SSR considerations: Remember that
windowobject doesn't exist during SSR
Troubleshooting
Common Issues
"Chatoshi is not defined" error
- Make sure the SDK script is loaded before initialization
- Use proper loading state management
Multiple instances created
- Always destroy previous instances before creating new ones
- Use refs to maintain single instance per component
SSR/Hydration issues
- Use
useEffectfor client-side only initialization - Check for
windowobject existence
This integration approach ensures proper lifecycle management and works seamlessly with React and Next.js applications.